summaryrefslogtreecommitdiffstats
path: root/gfx/layers/client/ContentClient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/client/ContentClient.cpp')
-rw-r--r--gfx/layers/client/ContentClient.cpp668
1 files changed, 668 insertions, 0 deletions
diff --git a/gfx/layers/client/ContentClient.cpp b/gfx/layers/client/ContentClient.cpp
new file mode 100644
index 000000000..3373230a9
--- /dev/null
+++ b/gfx/layers/client/ContentClient.cpp
@@ -0,0 +1,668 @@
+/* -*- Mode: C++; tab-width: 20; 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/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 "gfxPrefs.h" // for gfxPrefs
+#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/Maybe.h"
+#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 "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
+#include "ReadbackLayer.h"
+
+#include <vector>
+
+using namespace std;
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+static TextureFlags TextureFlagsForRotatedContentBufferFlags(uint32_t aBufferFlags)
+{
+ TextureFlags result = TextureFlags::NO_FLAGS;
+
+ if (aBufferFlags & RotatedContentBuffer::BUFFER_COMPONENT_ALPHA) {
+ result |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ return result;
+}
+
+/* static */ already_AddRefed<ContentClient>
+ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
+{
+ LayersBackend backend = aForwarder->GetCompositorBackendType();
+ if (backend != LayersBackend::LAYERS_OPENGL &&
+ backend != LayersBackend::LAYERS_D3D9 &&
+ backend != LayersBackend::LAYERS_D3D11 &&
+ 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
+ // 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 = (LayerManagerComposite::SupportsDirectTexturing() &&
+ backend != LayersBackend::LAYERS_D3D9) ||
+ backend == LayersBackend::LAYERS_BASIC;
+ }
+
+ if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
+ return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
+ }
+ return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
+}
+
+void
+ContentClient::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
+{
+}
+
+void
+ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
+
+ if (profiler_feature_active("displaylistdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+// 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)
+ , RotatedContentBuffer(ContainsVisibleBounds)
+ , mBackend(aBackend)
+{}
+
+void
+ContentClientBasic::CreateBuffer(ContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT,
+ RefPtr<gfx::DrawTarget>* aWhiteDT)
+{
+ 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);
+#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);
+ *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size);
+
+ if (*aBlackDT) {
+ return;
+ }
+ }
+#endif
+
+ *aBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForBackend(
+ mBackend, size,
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType));
+}
+
+void
+ContentClientRemoteBuffer::DestroyBuffers()
+{
+ if (!mTextureClient) {
+ return;
+ }
+
+ mOldTextures.AppendElement(mTextureClient);
+ mTextureClient = nullptr;
+ if (mTextureClientOnWhite) {
+ mOldTextures.AppendElement(mTextureClientOnWhite);
+ mTextureClientOnWhite = nullptr;
+ }
+
+ DestroyFrontBuffer();
+}
+
+class RemoteBufferReadbackProcessor : public TextureReadbackSink
+{
+public:
+ RemoteBufferReadbackProcessor(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates,
+ const IntRect& aBufferRect, const nsIntPoint& aBufferRotation)
+ : mReadbackUpdates(*aReadbackUpdates)
+ , 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)
+ {
+ 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, RotatedBuffer::BUFFER_BLACK);
+
+ update.mLayer->GetSink()->EndUpdate(update.mUpdateRect + offset);
+ }
+ }
+
+private:
+ nsTArray<ReadbackProcessor::Update> mReadbackUpdates;
+ // This array is used to keep the layers alive until the callback.
+ vector<RefPtr<Layer>> mLayerRefs;
+
+ IntRect mBufferRect;
+ nsIntPoint mBufferRotation;
+};
+
+void
+ContentClientRemoteBuffer::BeginPaint()
+{
+ EnsureBackBufferIfFrontBuffer();
+
+ // XXX: So we might not have a TextureClient yet.. because it will
+ // only be created by CreateBuffer.. which will deliver a locked surface!.
+ if (mTextureClient) {
+ SetBufferProvider(mTextureClient);
+ }
+ if (mTextureClientOnWhite) {
+ SetBufferProviderOnWhite(mTextureClientOnWhite);
+ }
+}
+
+void
+ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
+{
+ MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0);
+
+ // XXX: We might still not have a texture client if PaintThebes
+ // decided we didn't need one yet because the region to draw was empty.
+ SetBufferProvider(nullptr);
+ SetBufferProviderOnWhite(nullptr);
+ for (unsigned i = 0; i< mOldTextures.Length(); ++i) {
+ if (mOldTextures[i]->IsLocked()) {
+ mOldTextures[i]->Unlock();
+ }
+ }
+ mOldTextures.Clear();
+
+ if (mTextureClient && mTextureClient->IsLocked()) {
+ if (aReadbackUpdates->Length() > 0) {
+ RefPtr<TextureReadbackSink> readbackSink = new RemoteBufferReadbackProcessor(aReadbackUpdates, mBufferRect, mBufferRotation);
+
+ mTextureClient->SetReadbackSink(readbackSink);
+ }
+
+ mTextureClient->Unlock();
+ mTextureClient->SyncWithObject(mForwarder->GetSyncObject());
+ }
+ if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
+ mTextureClientOnWhite->Unlock();
+ mTextureClientOnWhite->SyncWithObject(mForwarder->GetSyncObject());
+ }
+
+ ContentClientRemote::EndPaint(aReadbackUpdates);
+}
+
+void
+ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
+ 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?");
+
+ mIsNewBuffer = true;
+
+ DestroyBuffers();
+
+ mSurfaceFormat = aFormat;
+ mSize = IntSize(aRect.width, aRect.height);
+ mTextureFlags = TextureFlagsForRotatedContentBufferFlags(aFlags);
+
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ mTextureFlags |= TextureFlags::COMPONENT_ALPHA;
+ }
+
+ CreateBackBuffer(mBufferRect);
+}
+
+void
+ContentClientRemoteBuffer::CreateBackBuffer(const IntRect& aBufferRect)
+{
+ // gfx::BackendType::NONE means fallback to the content backend
+ TextureAllocationFlags textureAllocFlags
+ = (mTextureFlags & TextureFlags::COMPONENT_ALPHA) ?
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER_BLACK :
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
+
+ mTextureClient = CreateTextureClientForDrawing(
+ mSurfaceFormat, mSize, BackendSelector::Content,
+ mTextureFlags | ExtraTextureFlags(),
+ textureAllocFlags
+ );
+ if (!mTextureClient || !AddTextureClient(mTextureClient)) {
+ AbortTextureClientCreation();
+ return;
+ }
+
+ if (mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
+ mTextureClientOnWhite = mTextureClient->CreateSimilar(
+ mForwarder->GetCompositorBackendType(),
+ mTextureFlags | ExtraTextureFlags(),
+ TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE
+ );
+ if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) {
+ AbortTextureClientCreation();
+ return;
+ }
+ }
+}
+
+void
+ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
+ const IntRect& aRect,
+ uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT,
+ RefPtr<gfx::DrawTarget>* aWhiteDT)
+{
+ BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
+ if (!mTextureClient) {
+ return;
+ }
+
+ // We just created the textures and we are about to get their draw targets
+ // so we have to lock them here.
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked, "Could not lock the TextureClient");
+
+ *aBlackDT = mTextureClient->BorrowDrawTarget();
+ if (aFlags & BUFFER_COMPONENT_ALPHA) {
+ locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
+
+ *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
+ }
+}
+
+nsIntRegion
+ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ nsIntRegion updatedRegion;
+ if (mIsNewBuffer || aDidSelfCopy) {
+ // 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;
+ }
+
+ NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
+ "Update outside of buffer rect!");
+ MOZ_ASSERT(mTextureClient, "should have a back buffer by now");
+
+ return updatedRegion;
+}
+
+void
+ContentClientRemoteBuffer::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ nsIntRegion updatedRegion = GetUpdatedRegion(aRegionToDraw,
+ aVisibleRegion,
+ aDidSelfCopy);
+
+ MOZ_ASSERT(mTextureClient);
+ if (mTextureClientOnWhite) {
+ mForwarder->UseComponentAlphaTextures(this, mTextureClient,
+ mTextureClientOnWhite);
+ } else {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mTextureClient;
+ IntSize size = mTextureClient->GetSize();
+ t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
+ GetForwarder()->UseTextures(this, textures);
+ }
+ mForwarder->UpdateTextureRegion(this,
+ ThebesBufferData(BufferRect(),
+ BufferRotation()),
+ updatedRegion);
+}
+
+void
+ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
+{
+ mFrontAndBackBufferDiffer = true;
+}
+
+void
+ContentClientRemoteBuffer::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, mTextureClient, 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, mFrontClient, aCompress);
+}
+
+void
+ContentClientDoubleBuffered::DestroyFrontBuffer()
+{
+ if (mFrontClient) {
+ mOldTextures.AppendElement(mFrontClient);
+ mFrontClient = nullptr;
+ }
+
+ if (mFrontClientOnWhite) {
+ mOldTextures.AppendElement(mFrontClientOnWhite);
+ mFrontClientOnWhite = nullptr;
+ }
+}
+
+void
+ContentClientDoubleBuffered::Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy)
+{
+ ContentClientRemoteBuffer::Updated(aRegionToDraw, aVisibleRegion, aDidSelfCopy);
+}
+
+void
+ContentClientDoubleBuffered::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
+{
+ mFrontUpdatedRegion = aFrontUpdatedRegion;
+
+ RefPtr<TextureClient> oldBack = mTextureClient;
+ mTextureClient = mFrontClient;
+ mFrontClient = oldBack;
+
+ oldBack = mTextureClientOnWhite;
+ mTextureClientOnWhite = mFrontClientOnWhite;
+ mFrontClientOnWhite = oldBack;
+
+ IntRect oldBufferRect = mBufferRect;
+ mBufferRect = mFrontBufferRect;
+ mFrontBufferRect = oldBufferRect;
+
+ nsIntPoint oldBufferRotation = mBufferRotation;
+ mBufferRotation = mFrontBufferRotation;
+ mFrontBufferRotation = oldBufferRotation;
+
+ MOZ_ASSERT(mFrontClient);
+
+ ContentClientRemoteBuffer::SwapBuffers(aFrontUpdatedRegion);
+}
+
+void
+ContentClientDoubleBuffered::BeginPaint()
+{
+ ContentClientRemoteBuffer::BeginPaint();
+
+ mIsNewBuffer = false;
+
+ if (!mFrontAndBackBufferDiffer) {
+ return;
+ }
+
+ if (mDidSelfCopy) {
+ // 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.
+ mBufferRect.MoveTo(mFrontBufferRect.TopLeft());
+ mBufferRotation = nsIntPoint();
+ return;
+ }
+ mBufferRect = mFrontBufferRect;
+ mBufferRotation = mFrontBufferRotation;
+}
+
+// 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(const nsIntRegion& aRegionToDraw)
+{
+ if (mTextureClient) {
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+ if (mTextureClientOnWhite) {
+ DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+
+ if (!mFrontAndBackBufferDiffer) {
+ MOZ_ASSERT(!mDidSelfCopy, "If we have to copy the world, then our buffers are different, right?");
+ return;
+ }
+ MOZ_ASSERT(mFrontClient);
+ if (!mFrontClient) {
+ 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 (mDidSelfCopy) {
+ mDidSelfCopy = false;
+ updateRegion = mBufferRect;
+ }
+
+ // 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, aRegionToDraw);
+ if (updateRegion.IsEmpty()) {
+ return;
+ }
+
+ // We need to ensure that we lock these two buffers in the same
+ // order as the compositor to prevent deadlocks.
+ TextureClientAutoLock frontLock(mFrontClient, OpenMode::OPEN_READ_ONLY);
+ if (!frontLock.Succeeded()) {
+ return;
+ }
+ Maybe<TextureClientAutoLock> frontOnWhiteLock;
+ if (mFrontClientOnWhite) {
+ frontOnWhiteLock.emplace(mFrontClientOnWhite, OpenMode::OPEN_READ_ONLY);
+ if (!frontOnWhiteLock->Succeeded()) {
+ return;
+ }
+ }
+
+ // Restrict the DrawTargets and frontBuffer to a scope to make
+ // sure there is no more external references to the DrawTargets
+ // when we Unlock the TextureClients.
+ gfx::DrawTarget* dt = mFrontClient->BorrowDrawTarget();
+ gfx::DrawTarget* dtw = mFrontClientOnWhite ? mFrontClientOnWhite->BorrowDrawTarget() : nullptr;
+ if (dt && dt->IsValid()) {
+ RefPtr<SourceSurface> surf = dt->Snapshot();
+ RefPtr<SourceSurface> surfOnWhite = dtw ? dtw->Snapshot() : nullptr;
+ SourceRotatedBuffer frontBuffer(surf,
+ surfOnWhite,
+ mFrontBufferRect,
+ mFrontBufferRotation);
+ UpdateDestinationFrom(frontBuffer, updateRegion);
+ } else {
+ // We know this can happen, but we want to track it somewhat, in case it leads
+ // to other problems.
+ gfxCriticalNote << "Invalid draw target(s) " << hexa(dt) << " and " << hexa(dtw);
+ }
+}
+
+void
+ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
+{
+ if (!mTextureClient && mFrontClient) {
+ CreateBackBuffer(mFrontBufferRect);
+
+ mBufferRect = mFrontBufferRect;
+ mBufferRotation = mFrontBufferRotation;
+ }
+}
+
+void
+ContentClientDoubleBuffered::UpdateDestinationFrom(const RotatedBuffer& aSource,
+ const nsIntRegion& aUpdateRegion)
+{
+ DrawIterator iter;
+ while (DrawTarget* destDT =
+ BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_BLACK, &iter)) {
+ bool isClippingCheap = IsClippingCheap(destDT, iter.mDrawRegion);
+ if (isClippingCheap) {
+ gfxUtils::ClipToRegion(destDT, iter.mDrawRegion);
+ }
+
+ aSource.DrawBufferWithRotation(destDT, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
+ if (isClippingCheap) {
+ destDT->PopClip();
+ }
+ // Flush the destination before the sources become inaccessible (Unlock).
+ destDT->Flush();
+ ReturnDrawTargetToBuffer(destDT);
+ }
+
+ if (aSource.HaveBufferOnWhite()) {
+ MOZ_ASSERT(HaveBufferOnWhite());
+ DrawIterator whiteIter;
+ while (DrawTarget* destDT =
+ BorrowDrawTargetForQuadrantUpdate(aUpdateRegion.GetBounds(), BUFFER_WHITE, &whiteIter)) {
+ bool isClippingCheap = IsClippingCheap(destDT, whiteIter.mDrawRegion);
+ if (isClippingCheap) {
+ gfxUtils::ClipToRegion(destDT, whiteIter.mDrawRegion);
+ }
+
+ aSource.DrawBufferWithRotation(destDT, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
+ if (isClippingCheap) {
+ destDT->PopClip();
+ }
+ // Flush the destination before the sources become inaccessible (Unlock).
+ destDT->Flush();
+ ReturnDrawTargetToBuffer(destDT);
+ }
+ }
+}
+
+void
+ContentClientSingleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
+{
+ if (mTextureClient) {
+ DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+ if (mTextureClientOnWhite) {
+ DebugOnly<bool> locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+ MOZ_ASSERT(locked);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla