summaryrefslogtreecommitdiffstats
path: root/gfx/layers/client
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /gfx/layers/client
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/layers/client')
-rw-r--r--gfx/layers/client/CanvasClient.cpp531
-rw-r--r--gfx/layers/client/CanvasClient.h216
-rw-r--r--gfx/layers/client/ClientCanvasLayer.cpp261
-rw-r--r--gfx/layers/client/ClientCanvasLayer.h116
-rw-r--r--gfx/layers/client/ClientColorLayer.cpp79
-rw-r--r--gfx/layers/client/ClientContainerLayer.cpp36
-rw-r--r--gfx/layers/client/ClientContainerLayer.h189
-rw-r--r--gfx/layers/client/ClientImageLayer.cpp170
-rw-r--r--gfx/layers/client/ClientLayerManager.cpp907
-rw-r--r--gfx/layers/client/ClientLayerManager.h422
-rw-r--r--gfx/layers/client/ClientPaintedLayer.cpp175
-rw-r--r--gfx/layers/client/ClientPaintedLayer.h127
-rw-r--r--gfx/layers/client/ClientReadbackLayer.h34
-rw-r--r--gfx/layers/client/ClientTiledPaintedLayer.cpp615
-rw-r--r--gfx/layers/client/ClientTiledPaintedLayer.h153
-rw-r--r--gfx/layers/client/CompositableChild.cpp119
-rw-r--r--gfx/layers/client/CompositableChild.h91
-rw-r--r--gfx/layers/client/CompositableClient.cpp274
-rw-r--r--gfx/layers/client/CompositableClient.h209
-rw-r--r--gfx/layers/client/ContentClient.cpp668
-rw-r--r--gfx/layers/client/ContentClient.h412
-rw-r--r--gfx/layers/client/GPUVideoTextureClient.cpp72
-rw-r--r--gfx/layers/client/GPUVideoTextureClient.h56
-rw-r--r--gfx/layers/client/ImageClient.cpp300
-rw-r--r--gfx/layers/client/ImageClient.h138
-rw-r--r--gfx/layers/client/SingleTiledContentClient.cpp263
-rw-r--r--gfx/layers/client/SingleTiledContentClient.h135
-rw-r--r--gfx/layers/client/TextureClient.cpp1707
-rw-r--r--gfx/layers/client/TextureClient.h826
-rw-r--r--gfx/layers/client/TextureClientPool.cpp339
-rw-r--r--gfx/layers/client/TextureClientPool.h180
-rw-r--r--gfx/layers/client/TextureClientRecycleAllocator.cpp279
-rw-r--r--gfx/layers/client/TextureClientRecycleAllocator.h140
-rw-r--r--gfx/layers/client/TextureClientSharedSurface.cpp96
-rw-r--r--gfx/layers/client/TextureClientSharedSurface.h77
-rw-r--r--gfx/layers/client/TiledContentClient.cpp1430
-rw-r--r--gfx/layers/client/TiledContentClient.h545
37 files changed, 12387 insertions, 0 deletions
diff --git a/gfx/layers/client/CanvasClient.cpp b/gfx/layers/client/CanvasClient.cpp
new file mode 100644
index 000000000..40513984e
--- /dev/null
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -0,0 +1,531 @@
+/* -*- 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 "CanvasClient.h"
+
+#include "ClientCanvasLayer.h" // for ClientCanvasLayer
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "ScopedGLHelpers.h"
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "GLReadTexImageHelper.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "nsDebug.h" // for printf_stderr, NS_ASSERTION
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TextureClientSharedSurface.h"
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/* static */ already_AddRefed<CanvasClient>
+CanvasClient::CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ switch (aType) {
+ case CanvasClientTypeShSurf:
+ return MakeAndAddRef<CanvasClientSharedSurface>(aForwarder, aFlags);
+ case CanvasClientAsync:
+ return MakeAndAddRef<CanvasClientBridge>(aForwarder, aFlags);
+ default:
+ return MakeAndAddRef<CanvasClient2D>(aForwarder, aFlags);
+ break;
+ }
+}
+
+void
+CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ if (!GetForwarder() || !mLayer || !aRenderer ||
+ !aRenderer->GetCanvasClient()) {
+ return;
+ }
+
+ uint64_t asyncID = aRenderer->GetCanvasClientAsyncID();
+ if (asyncID == 0 || mAsyncID == asyncID) {
+ return;
+ }
+
+ static_cast<ShadowLayerForwarder*>(GetForwarder())
+ ->AttachAsyncCompositable(asyncID, mLayer);
+ mAsyncID = asyncID;
+}
+
+void
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+{
+ MOZ_ASSERT(aTexture);
+
+ if (!aTexture->IsSharedWithCompositor()) {
+ if (!AddTextureClient(aTexture)) {
+ return;
+ }
+ }
+
+ mBackBuffer = nullptr;
+ mFrontBuffer = nullptr;
+ mBufferProviderTexture = aTexture;
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = aTexture;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
+ t->mFrameID = mFrameID;
+
+ GetForwarder()->UseTextures(this, textures);
+ aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
+}
+
+void
+CanvasClient2D::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ mBufferProviderTexture = nullptr;
+
+ AutoRemoveTexture autoRemove(this);
+ if (mBackBuffer && (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
+ autoRemove.mTexture = mBackBuffer;
+ mBackBuffer = nullptr;
+ }
+
+ bool bufferCreated = false;
+ if (!mBackBuffer) {
+ bool isOpaque = (aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE);
+ gfxContentType contentType = isOpaque
+ ? gfxContentType::COLOR
+ : gfxContentType::COLOR_ALPHA;
+ gfx::SurfaceFormat surfaceFormat
+ = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags, aLayer);
+ if (!mBackBuffer) {
+ NS_WARNING("Failed to allocate the TextureClient");
+ return;
+ }
+ mBackBuffer->EnableReadLock();
+ MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
+
+ bufferCreated = true;
+ }
+
+ bool updated = false;
+ {
+ TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
+ if (target) {
+ if (!aLayer->UpdateTarget(target)) {
+ NS_WARNING("Failed to copy the canvas into a TextureClient.");
+ return;
+ }
+ updated = true;
+ }
+ }
+
+ if (bufferCreated && !AddTextureClient(mBackBuffer)) {
+ mBackBuffer = nullptr;
+ return;
+ }
+
+ if (updated) {
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mBackBuffer;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
+ t->mFrameID = mFrameID;
+ GetForwarder()->UseTextures(this, textures);
+ mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ mBackBuffer.swap(mFrontBuffer);
+}
+
+already_AddRefed<TextureClient>
+CanvasClient2D::CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer)
+{
+ if (aLayer->IsGLLayer()) {
+ // We want a cairo backend here as we don't want to be copying into
+ // an accelerated backend and we like LockBits to work. This is currently
+ // the most effective way to make this work.
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, BackendType::CAIRO,
+ mTextureFlags | aFlags);
+ }
+
+#ifdef XP_WIN
+ return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas, aFlags);
+#else
+ // XXX - We should use CreateTextureClientForDrawing, but we first need
+ // to use double buffering.
+ gfx::BackendType backend = gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, backend,
+ mTextureFlags | aFlags);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////
+
+CanvasClientSharedSurface::CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+{ }
+
+CanvasClientSharedSurface::~CanvasClientSharedSurface()
+{
+ ClearSurfaces();
+}
+
+////////////////////////////////////////
+// Readback
+
+// For formats compatible with R8G8B8A8.
+static inline void SwapRB_R8G8B8A8(uint8_t* pixel) {
+ // [RR, GG, BB, AA]
+ Swap(pixel[0], pixel[2]);
+}
+
+class TexClientFactory
+{
+ CompositableForwarder* const mAllocator;
+ const bool mHasAlpha;
+ const gfx::IntSize mSize;
+ const gfx::BackendType mBackendType;
+ const TextureFlags mBaseTexFlags;
+ const LayersBackend mLayersBackend;
+
+public:
+ TexClientFactory(CompositableForwarder* allocator, bool hasAlpha,
+ const gfx::IntSize& size, gfx::BackendType backendType,
+ TextureFlags baseTexFlags, LayersBackend layersBackend)
+ : mAllocator(allocator)
+ , mHasAlpha(hasAlpha)
+ , mSize(size)
+ , mBackendType(backendType)
+ , mBaseTexFlags(baseTexFlags)
+ , mLayersBackend(layersBackend)
+ {
+ }
+
+protected:
+ already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
+ return TextureClient::CreateForRawBufferAccess(mAllocator, format,
+ mSize, mBackendType,
+ mBaseTexFlags);
+ }
+
+public:
+ already_AddRefed<TextureClient> CreateB8G8R8AX8() {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
+ : gfx::SurfaceFormat::B8G8R8X8;
+ return Create(format);
+ }
+
+ already_AddRefed<TextureClient> CreateR8G8B8AX8() {
+ RefPtr<TextureClient> ret;
+
+ bool areRGBAFormatsBroken = mLayersBackend == LayersBackend::LAYERS_BASIC;
+ if (!areRGBAFormatsBroken) {
+ gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+ ret = Create(format);
+ }
+
+ if (!ret) {
+ ret = CreateB8G8R8AX8();
+ if (ret) {
+ ret->AddFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return ret.forget();
+ }
+};
+
+static already_AddRefed<TextureClient>
+TexClientFromReadback(SharedSurface* src, CompositableForwarder* allocator,
+ TextureFlags baseFlags, LayersBackend layersBackend)
+{
+ auto backendType = gfx::BackendType::CAIRO;
+ TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
+ baseFlags, layersBackend);
+
+ RefPtr<TextureClient> texClient;
+
+ {
+ gl::ScopedReadbackFB autoReadback(src);
+
+ // We have a source FB, now we need a format.
+ GLenum destFormat = LOCAL_GL_BGRA;
+ GLenum destType = LOCAL_GL_UNSIGNED_BYTE;
+ GLenum readFormat;
+ GLenum readType;
+
+ // We actually don't care if they match, since we can handle
+ // any read{Format,Type} we get.
+ auto gl = src->mGL;
+ GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType);
+
+ MOZ_ASSERT(readFormat == LOCAL_GL_RGBA ||
+ readFormat == LOCAL_GL_BGRA);
+ MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
+
+ // With a format and type, we can create texClient.
+ if (readFormat == LOCAL_GL_BGRA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // 0xAARRGGBB
+ // In Lendian: [BB, GG, RR, AA]
+ texClient = factory.CreateB8G8R8AX8();
+
+ } else if (readFormat == LOCAL_GL_RGBA &&
+ readType == LOCAL_GL_UNSIGNED_BYTE)
+ {
+ // [RR, GG, BB, AA]
+ texClient = factory.CreateR8G8B8AX8();
+ } else {
+ MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
+ }
+
+ MOZ_ASSERT(texClient);
+ if (!texClient)
+ return nullptr;
+
+ // With a texClient, we can lock for writing.
+ TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
+ DebugOnly<bool> succeeded = autoLock.Succeeded();
+ MOZ_ASSERT(succeeded, "texture should have locked");
+
+ MappedTextureData mapped;
+ texClient->BorrowMappedData(mapped);
+
+ // ReadPixels from the current FB into mapped.data.
+ auto width = src->mSize.width;
+ auto height = src->mSize.height;
+
+ {
+ ScopedPackState scopedPackState(gl);
+
+ MOZ_ASSERT(mapped.stride/4 == mapped.size.width);
+ gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, mapped.data);
+ }
+
+ // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
+ // RB_SWAPPED doesn't work with Basic. (bug ???????)
+ // RB_SWAPPED doesn't work with D3D9. (bug ???????)
+ bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC ||
+ layersBackend == LayersBackend::LAYERS_D3D9 ||
+ layersBackend == LayersBackend::LAYERS_D3D11;
+ if (texClient->HasFlags(TextureFlags::RB_SWAPPED) &&
+ layersNeedsManualSwap)
+ {
+ size_t pixels = width * height;
+ uint8_t* itr = mapped.data;
+ for (size_t i = 0; i < pixels; i++) {
+ SwapRB_R8G8B8A8(itr);
+ itr += 4;
+ }
+
+ texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
+ }
+ }
+
+ return texClient.forget();
+}
+
+////////////////////////////////////////
+
+static already_AddRefed<SharedSurfaceTextureClient>
+CloneSurface(gl::SharedSurface* src, gl::SurfaceFactory* factory)
+{
+ RefPtr<SharedSurfaceTextureClient> dest = factory->NewTexClient(src->mSize);
+ if (!dest) {
+ return nullptr;
+ }
+
+ gl::SharedSurface* destSurf = dest->Surf();
+
+ destSurf->ProducerAcquire();
+ SharedSurface::ProdCopy(src, dest->Surf(), factory);
+ destSurf->ProducerRelease();
+
+ return dest.forget();
+}
+
+void
+CanvasClientSharedSurface::Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer)
+{
+ Renderer renderer;
+ renderer.construct<ClientCanvasLayer*>(aLayer);
+ UpdateRenderer(aSize, renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer)
+{
+ Renderer renderer;
+ renderer.construct<AsyncCanvasRenderer*>(aRenderer);
+ UpdateRenderer(aRenderer->GetSize(), renderer);
+}
+
+void
+CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer)
+{
+ GLContext* gl = nullptr;
+ ClientCanvasLayer* layer = nullptr;
+ AsyncCanvasRenderer* asyncRenderer = nullptr;
+ if (aRenderer.constructed<ClientCanvasLayer*>()) {
+ layer = aRenderer.ref<ClientCanvasLayer*>();
+ gl = layer->mGLContext;
+ } else {
+ asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
+ gl = asyncRenderer->mGLContext;
+ }
+ gl->MakeCurrent();
+
+ RefPtr<TextureClient> newFront;
+
+ if (layer && layer->mGLFrontbuffer) {
+ mShSurfClient = CloneSurface(layer->mGLFrontbuffer.get(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ gfxCriticalError() << "Invalid canvas front buffer";
+ return;
+ }
+ } else if (layer && layer->mIsMirror) {
+ mShSurfClient = CloneSurface(gl->Screen()->Front()->Surf(), layer->mFactory.get());
+ if (!mShSurfClient) {
+ return;
+ }
+ } else {
+ mShSurfClient = gl->Screen()->Front();
+ if (mShSurfClient && mShSurfClient->GetAllocator() &&
+ mShSurfClient->GetAllocator() != GetForwarder()->GetTextureForwarder()) {
+ mShSurfClient = CloneSurface(mShSurfClient->Surf(), gl->Screen()->Factory());
+ }
+ if (!mShSurfClient) {
+ return;
+ }
+ }
+ MOZ_ASSERT(mShSurfClient);
+
+ newFront = mShSurfClient;
+
+ SharedSurface* surf = mShSurfClient->Surf();
+
+ // Readback if needed.
+ mReadbackClient = nullptr;
+
+ auto forwarder = GetForwarder();
+
+ bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
+ if (needsReadback) {
+ TextureFlags flags = TextureFlags::IMMUTABLE;
+
+ CompositableForwarder* shadowForwarder = nullptr;
+ if (layer) {
+ flags |= layer->Flags();
+ shadowForwarder = layer->ClientManager()->AsShadowForwarder();
+ } else {
+ MOZ_ASSERT(asyncRenderer);
+ flags |= mTextureFlags;
+ shadowForwarder = GetForwarder();
+ }
+
+ auto layersBackend = shadowForwarder->GetCompositorBackendType();
+ mReadbackClient = TexClientFromReadback(surf, forwarder, flags, layersBackend);
+
+ newFront = mReadbackClient;
+ } else {
+ mReadbackClient = nullptr;
+ }
+
+ if (asyncRenderer) {
+ // If surface type is Basic, above codes will readback
+ // the GLContext to mReadbackClient in order to send frame to
+ // compositor. We copy from this TextureClient directly by
+ // calling CopyFromTextureClient().
+ // Therefore, if main-thread want the content of GLContext,
+ // it doesn't have to readback from GLContext again.
+ //
+ // Otherwise, if surface type isn't Basic, we will read from
+ // SharedSurface directly from main-thread. We still pass
+ // mReadbackClient which is nullptr here to tell
+ // AsyncCanvasRenderer reset some properties.
+ asyncRenderer->CopyFromTextureClient(mReadbackClient);
+ }
+
+ MOZ_ASSERT(newFront);
+ if (!newFront) {
+ // May happen in a release build in case of memory pressure.
+ gfxCriticalError() << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: " << aSize;
+ return;
+ }
+
+ mNewFront = newFront;
+}
+
+void
+CanvasClientSharedSurface::Updated()
+{
+ if (!mNewFront) {
+ return;
+ }
+
+ auto forwarder = GetForwarder();
+
+ mFront = mNewFront;
+ mNewFront = nullptr;
+
+ // Add the new TexClient.
+ MOZ_ALWAYS_TRUE( AddTextureClient(mFront) );
+
+ AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = mFront;
+ t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
+ t->mFrameID = mFrameID;
+ forwarder->UseTextures(this, textures);
+}
+
+void
+CanvasClientSharedSurface::OnDetach() {
+ ClearSurfaces();
+}
+
+void
+CanvasClientSharedSurface::ClearSurfaces()
+{
+ if (mFront) {
+ mFront->CancelWaitForRecycle();
+ }
+ mFront = nullptr;
+ mNewFront = nullptr;
+ mShSurfClient = nullptr;
+ mReadbackClient = nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/CanvasClient.h b/gfx/layers/client/CanvasClient.h
new file mode 100644
index 000000000..cd88d02ab
--- /dev/null
+++ b/gfx/layers/client/CanvasClient.h
@@ -0,0 +1,216 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_CANVASCLIENT_H
+#define MOZILLA_GFX_CANVASCLIENT_H
+
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/PersistentBufferProvider.h"
+
+// Fix X11 header brain damage that conflicts with MaybeOneOf::None
+#undef None
+#include "mozilla/MaybeOneOf.h"
+
+#include "mozilla/mozalloc.h" // for operator delete
+
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+
+namespace mozilla {
+namespace layers {
+
+class AsyncCanvasRenderer;
+class ClientCanvasLayer;
+class CompositableForwarder;
+class ShadowableLayer;
+class SharedSurfaceTextureClient;
+
+/**
+ * Compositable client for 2d and webgl canvas.
+ */
+class CanvasClient : public CompositableClient
+{
+public:
+ typedef MaybeOneOf<ClientCanvasLayer*, AsyncCanvasRenderer*> Renderer;
+
+ /**
+ * Creates, configures, and returns a new canvas client. If necessary, a
+ * message will be sent to the compositor to create a corresponding image
+ * host.
+ */
+ enum CanvasClientType {
+ CanvasClientSurface,
+ CanvasClientGLContext,
+ CanvasClientTypeShSurf,
+ CanvasClientAsync, // webgl on workers
+ };
+ static already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClientType aType,
+ CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ CanvasClient(CompositableForwarder* aFwd, TextureFlags aFlags)
+ : CompositableClient(aFwd, aFlags)
+ , mFrameID(0)
+ {
+ mTextureFlags = aFlags;
+ }
+
+ virtual ~CanvasClient() {}
+
+ virtual void Clear() {};
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) = 0;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override
+ {
+ ++mFrameID;
+ return CompositableClient::AddTextureClient(aTexture);
+ }
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
+
+ virtual void UpdateFromTexture(TextureClient* aTexture) {}
+
+ virtual void Updated() { }
+
+protected:
+ int32_t mFrameID;
+};
+
+// Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
+class CanvasClient2D : public CanvasClient
+{
+public:
+ CanvasClient2D(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+ {
+ }
+
+ TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::IMAGE, mTextureFlags);
+ }
+
+ virtual void Clear() override
+ {
+ mBackBuffer = mFrontBuffer = nullptr;
+ }
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override;
+
+ virtual void UpdateFromTexture(TextureClient* aBuffer) override;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override
+ {
+ return CanvasClient::AddTextureClient(aTexture);
+ }
+
+ virtual void OnDetach() override
+ {
+ mBackBuffer = mFrontBuffer = nullptr;
+ }
+
+private:
+ already_AddRefed<TextureClient>
+ CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ ClientCanvasLayer* aLayer);
+
+ RefPtr<TextureClient> mBackBuffer;
+ RefPtr<TextureClient> mFrontBuffer;
+ // We store this texture separately to make sure it is not written into
+ // in Update() if for some silly reason we end up alternating between
+ // UpdateFromTexture and Update.
+ // This code is begging for a cleanup. The situation described above should
+ // not be made possible.
+ RefPtr<TextureClient> mBufferProviderTexture;
+};
+
+// Used for GL canvases where we don't need to do any readback, i.e., with a
+// GL backend.
+class CanvasClientSharedSurface : public CanvasClient
+{
+private:
+ RefPtr<SharedSurfaceTextureClient> mShSurfClient;
+ RefPtr<TextureClient> mReadbackClient;
+ RefPtr<TextureClient> mFront;
+ RefPtr<TextureClient> mNewFront;
+
+ void ClearSurfaces();
+
+public:
+ CanvasClientSharedSurface(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags);
+
+ ~CanvasClientSharedSurface();
+
+ virtual TextureInfo GetTextureInfo() const override {
+ return TextureInfo(CompositableType::IMAGE);
+ }
+
+ virtual void Clear() override {
+ ClearSurfaces();
+ }
+
+ virtual void Update(gfx::IntSize aSize,
+ ClientCanvasLayer* aLayer) override;
+ void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer);
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
+
+ virtual void Updated() override;
+
+ virtual void OnDetach() override;
+};
+
+/**
+ * Used for OMT<canvas> uploads using the image bridge protocol.
+ * Actual CanvasClient is on the ImageBridgeChild thread, so we
+ * only forward its AsyncID here
+ */
+class CanvasClientBridge final : public CanvasClient
+{
+public:
+ CanvasClientBridge(CompositableForwarder* aLayerForwarder,
+ TextureFlags aFlags)
+ : CanvasClient(aLayerForwarder, aFlags)
+ , mAsyncID(0)
+ , mLayer(nullptr)
+ {
+ }
+
+ TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::IMAGE);
+ }
+
+ virtual void Update(gfx::IntSize aSize, ClientCanvasLayer* aLayer) override
+ {
+ }
+
+ virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
+
+ void SetLayer(ShadowableLayer* aLayer)
+ {
+ mLayer = aLayer;
+ }
+
+protected:
+ uint64_t mAsyncID;
+ ShadowableLayer* mLayer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp
new file mode 100644
index 000000000..32acf1eb2
--- /dev/null
+++ b/gfx/layers/client/ClientCanvasLayer.cpp
@@ -0,0 +1,261 @@
+/* -*- 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 "ClientCanvasLayer.h"
+#include "GLContext.h" // for GLContext
+#include "GLScreenBuffer.h" // for GLScreenBuffer
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "SharedSurfaceEGL.h" // for SurfaceFactory_EGLImage
+#include "SharedSurfaceGL.h" // for SurfaceFactory_GLTexture, etc
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "gfxPrefs.h" // for WebGLForceLayersReadback
+
+using namespace mozilla::gfx;
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+ClientCanvasLayer::~ClientCanvasLayer()
+{
+ MOZ_COUNT_DTOR(ClientCanvasLayer);
+ if (mCanvasClient) {
+ mCanvasClient->OnDetach();
+ mCanvasClient = nullptr;
+ }
+}
+
+void
+ClientCanvasLayer::Initialize(const Data& aData)
+{
+ CopyableCanvasLayer::Initialize(aData);
+
+ mCanvasClient = nullptr;
+
+ if (!mGLContext)
+ return;
+
+ GLScreenBuffer* screen = mGLContext->Screen();
+
+ SurfaceCaps caps;
+ if (mGLFrontbuffer) {
+ // The screen caps are irrelevant if we're using a separate frontbuffer.
+ caps = mGLFrontbuffer->mHasAlpha ? SurfaceCaps::ForRGBA()
+ : SurfaceCaps::ForRGB();
+ } else {
+ MOZ_ASSERT(screen);
+ caps = screen->mCaps;
+ }
+ MOZ_ASSERT(caps.alpha == aData.mHasAlpha);
+
+ auto forwarder = ClientManager()->AsShadowForwarder();
+
+ mFlags = TextureFlags::ORIGIN_BOTTOM_LEFT;
+ if (!aData.mIsGLAlphaPremult) {
+ mFlags |= TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ UniquePtr<SurfaceFactory> factory = GLScreenBuffer::CreateFactory(mGLContext, caps, forwarder, mFlags);
+
+ if (mGLFrontbuffer || aData.mIsMirror) {
+ // We're using a source other than the one in the default screen.
+ // (SkiaGL)
+ mFactory = Move(factory);
+ if (!mFactory) {
+ // Absolutely must have a factory here, so create a basic one
+ mFactory = MakeUnique<SurfaceFactory_Basic>(mGLContext, caps, mFlags);
+ }
+ } else {
+ if (factory)
+ screen->Morph(Move(factory));
+ }
+}
+
+void
+ClientCanvasLayer::RenderLayer()
+{
+ PROFILER_LABEL("ClientCanvasLayer", "RenderLayer",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ RenderMaskLayers(this);
+
+ if (!mCanvasClient) {
+ TextureFlags flags = TextureFlags::DEFAULT;
+ if (mOriginPos == gl::OriginPos::BottomLeft) {
+ flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
+ }
+
+ if (!mIsAlphaPremultiplied) {
+ flags |= TextureFlags::NON_PREMULTIPLIED;
+ }
+
+ mCanvasClient = CanvasClient::CreateCanvasClient(GetCanvasClientType(),
+ ClientManager()->AsShadowForwarder(),
+ flags);
+ if (!mCanvasClient) {
+ return;
+ }
+ if (HasShadow()) {
+ if (mAsyncRenderer) {
+ static_cast<CanvasClientBridge*>(mCanvasClient.get())->SetLayer(this);
+ } else {
+ mCanvasClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mCanvasClient, this);
+ }
+ }
+ }
+
+ if (mCanvasClient && mAsyncRenderer) {
+ mCanvasClient->UpdateAsync(mAsyncRenderer);
+ }
+
+ if (!IsDirty()) {
+ return;
+ }
+ Painted();
+
+ FirePreTransactionCallback();
+ if (mBufferProvider && mBufferProvider->GetTextureClient()) {
+ if (!mBufferProvider->SetForwarder(ClientManager()->AsShadowForwarder())) {
+ gfxCriticalNote << "BufferProvider::SetForwarder failed";
+ return;
+ }
+ mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
+ } else {
+ mCanvasClient->Update(gfx::IntSize(mBounds.width, mBounds.height), this);
+ }
+
+ FireDidTransactionCallback();
+
+ ClientManager()->Hold(this);
+ mCanvasClient->Updated();
+}
+
+bool
+ClientCanvasLayer::UpdateTarget(DrawTarget* aDestTarget)
+{
+ MOZ_ASSERT(aDestTarget);
+ if (!aDestTarget) {
+ return false;
+ }
+
+ RefPtr<SourceSurface> surface;
+
+ if (!mGLContext) {
+ AutoReturnSnapshot autoReturn;
+
+ if (mAsyncRenderer) {
+ surface = mAsyncRenderer->GetSurface();
+ } else if (mBufferProvider) {
+ surface = mBufferProvider->BorrowSnapshot();
+ autoReturn.mSnapshot = &surface;
+ autoReturn.mBufferProvider = mBufferProvider;
+ }
+
+ MOZ_ASSERT(surface);
+ if (!surface) {
+ return false;
+ }
+
+ aDestTarget->CopySurface(surface,
+ IntRect(0, 0, mBounds.width, mBounds.height),
+ IntPoint(0, 0));
+ return true;
+ }
+
+ SharedSurface* frontbuffer = nullptr;
+ if (mGLFrontbuffer) {
+ frontbuffer = mGLFrontbuffer.get();
+ } else {
+ GLScreenBuffer* screen = mGLContext->Screen();
+ const auto& front = screen->Front();
+ if (front) {
+ frontbuffer = front->Surf();
+ }
+ }
+
+ if (!frontbuffer) {
+ NS_WARNING("Null frame received.");
+ return false;
+ }
+
+ IntSize readSize(frontbuffer->mSize);
+ SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE)
+ ? SurfaceFormat::B8G8R8X8
+ : SurfaceFormat::B8G8R8A8;
+ bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
+
+ // Try to read back directly into aDestTarget's output buffer
+ uint8_t* destData;
+ IntSize destSize;
+ int32_t destStride;
+ SurfaceFormat destFormat;
+ if (aDestTarget->LockBits(&destData, &destSize, &destStride, &destFormat)) {
+ if (destSize == readSize && destFormat == format) {
+ RefPtr<DataSourceSurface> data =
+ Factory::CreateWrappingDataSourceSurface(destData, destStride, destSize, destFormat);
+ mGLContext->Readback(frontbuffer, data);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(data, data);
+ }
+ aDestTarget->ReleaseBits(destData);
+ return true;
+ }
+ aDestTarget->ReleaseBits(destData);
+ }
+
+ RefPtr<DataSourceSurface> resultSurf = GetTempSurface(readSize, format);
+ // There will already be a warning from inside of GetTempSurface, but
+ // it doesn't hurt to complain:
+ if (NS_WARN_IF(!resultSurf)) {
+ return false;
+ }
+
+ // Readback handles Flush/MarkDirty.
+ mGLContext->Readback(frontbuffer, resultSurf);
+ if (needsPremult) {
+ gfxUtils::PremultiplyDataSurface(resultSurf, resultSurf);
+ }
+
+ aDestTarget->CopySurface(resultSurf,
+ IntRect(0, 0, readSize.width, readSize.height),
+ IntPoint(0, 0));
+
+ return true;
+}
+
+CanvasClient::CanvasClientType
+ClientCanvasLayer::GetCanvasClientType()
+{
+ if (mAsyncRenderer) {
+ return CanvasClient::CanvasClientAsync;
+ }
+
+ if (mGLContext) {
+ return CanvasClient::CanvasClientTypeShSurf;
+ }
+ return CanvasClient::CanvasClientSurface;
+}
+
+already_AddRefed<CanvasLayer>
+ClientLayerManager::CreateCanvasLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientCanvasLayer> layer =
+ new ClientCanvasLayer(this);
+ CREATE_SHADOW(Canvas);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientCanvasLayer.h b/gfx/layers/client/ClientCanvasLayer.h
new file mode 100644
index 000000000..9a655727c
--- /dev/null
+++ b/gfx/layers/client/ClientCanvasLayer.h
@@ -0,0 +1,116 @@
+/* -*- 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/. */
+
+#ifndef GFX_CLIENTCANVASLAYER_H
+#define GFX_CLIENTCANVASLAYER_H
+
+#include "CanvasClient.h" // for CanvasClient, etc
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "CopyableCanvasLayer.h" // for CopyableCanvasLayer
+#include "Layers.h" // for CanvasLayer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/LayersMessages.h" // for CanvasLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace gl {
+class SurfaceFactory;
+} // namespace gl
+
+namespace layers {
+
+class CompositableClient;
+class ShadowableLayer;
+
+class ClientCanvasLayer : public CopyableCanvasLayer,
+ public ClientLayer
+{
+ typedef CanvasClient::CanvasClientType CanvasClientType;
+public:
+ explicit ClientCanvasLayer(ClientLayerManager* aLayerManager) :
+ CopyableCanvasLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientCanvasLayer);
+ }
+
+protected:
+ virtual ~ClientCanvasLayer();
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ CanvasLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void Initialize(const Data& aData) override;
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mCanvasClient) {
+ mCanvasClient->Clear();
+ }
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mCanvasClient) {
+ mCanvasClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = CanvasLayerAttributes(mSamplingFilter, mBounds);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ mCanvasClient = nullptr;
+ ClientLayer::Disconnect();
+ }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mCanvasClient;
+ }
+
+ const TextureFlags& Flags() const { return mFlags; }
+
+protected:
+
+ bool UpdateTarget(gfx::DrawTarget* aDestTarget = nullptr);
+
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ CanvasClientType GetCanvasClientType();
+
+ RefPtr<CanvasClient> mCanvasClient;
+
+ UniquePtr<gl::SurfaceFactory> mFactory;
+
+ TextureFlags mFlags;
+
+ friend class CanvasClient2D;
+ friend class CanvasClientSharedSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientColorLayer.cpp b/gfx/layers/client/ClientColorLayer.cpp
new file mode 100644
index 000000000..aa086f35a
--- /dev/null
+++ b/gfx/layers/client/ClientColorLayer.cpp
@@ -0,0 +1,79 @@
+/* -*- 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" // for ClientLayerManager, etc
+#include "Layers.h" // for ColorLayer, etc
+#include "mozilla/layers/LayersMessages.h" // for ColorLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientColorLayer : public ColorLayer,
+ public ClientLayer {
+public:
+ explicit ClientColorLayer(ClientLayerManager* aLayerManager) :
+ ColorLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientColorLayer);
+ }
+
+protected:
+ virtual ~ClientColorLayer()
+ {
+ MOZ_COUNT_DTOR(ClientColorLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion)
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ColorLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void RenderLayer()
+ {
+ RenderMaskLayers(this);
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+ {
+ aAttrs = ColorLayerAttributes(GetColor(), GetBounds());
+ }
+
+ virtual Layer* AsLayer() { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+ virtual void Disconnect()
+ {
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+already_AddRefed<ColorLayer>
+ClientLayerManager::CreateColorLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientColorLayer> layer =
+ new ClientColorLayer(this);
+ CREATE_SHADOW(Color);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientContainerLayer.cpp b/gfx/layers/client/ClientContainerLayer.cpp
new file mode 100644
index 000000000..f72854af9
--- /dev/null
+++ b/gfx/layers/client/ClientContainerLayer.cpp
@@ -0,0 +1,36 @@
+/* -*- 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 "ClientContainerLayer.h"
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "mozilla/mozalloc.h" // for operator new
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+
+namespace mozilla {
+namespace layers {
+
+already_AddRefed<ContainerLayer>
+ClientLayerManager::CreateContainerLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientContainerLayer> layer =
+ new ClientContainerLayer(this);
+ CREATE_SHADOW(Container);
+ return layer.forget();
+}
+
+already_AddRefed<RefLayer>
+ClientLayerManager::CreateRefLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientRefLayer> layer =
+ new ClientRefLayer(this);
+ CREATE_SHADOW(Ref);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientContainerLayer.h b/gfx/layers/client/ClientContainerLayer.h
new file mode 100644
index 000000000..de10a55b4
--- /dev/null
+++ b/gfx/layers/client/ClientContainerLayer.h
@@ -0,0 +1,189 @@
+/* -*- 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/. */
+
+#ifndef GFX_CLIENTCONTAINERLAYER_H
+#define GFX_CLIENTCONTAINERLAYER_H
+
+#include <stdint.h> // for uint32_t
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for Layer, ContainerLayer, etc
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsISupportsUtils.h" // for NS_ADDREF, NS_RELEASE
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for AutoTArray
+#include "ReadbackProcessor.h"
+#include "ClientPaintedLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+
+class ClientContainerLayer : public ContainerLayer,
+ public ClientLayer
+{
+public:
+ explicit ClientContainerLayer(ClientLayerManager* aManager) :
+ ContainerLayer(aManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientContainerLayer);
+ mSupportsComponentAlphaChildren = true;
+ }
+
+protected:
+ virtual ~ClientContainerLayer()
+ {
+ while (mFirstChild) {
+ ContainerLayer::RemoveChild(mFirstChild);
+ }
+
+ MOZ_COUNT_DTOR(ClientContainerLayer);
+ }
+
+public:
+ virtual void RenderLayer() override
+ {
+ RenderMaskLayers(this);
+
+ DefaultComputeSupportsComponentAlphaChildren();
+
+ AutoTArray<Layer*, 12> children;
+ SortChildrenBy3DZOrder(children);
+
+ ReadbackProcessor readback;
+ readback.BuildUpdates(this);
+
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ Layer* child = children.ElementAt(i);
+
+ ToClientLayer(child)->RenderLayerWithReadback(&readback);
+
+ if (!ClientManager()->GetRepeatTransaction() &&
+ !child->GetInvalidRegion().IsEmpty()) {
+ child->Mutated();
+ }
+ }
+ }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ContainerLayer::SetVisibleRegion(aRegion);
+ }
+ virtual bool InsertAfter(Layer* aChild, Layer* aAfter) override
+ {
+ if(!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+
+ if (!ContainerLayer::InsertAfter(aChild, aAfter)) {
+ return false;
+ }
+
+ ClientManager()->AsShadowForwarder()->InsertAfter(ClientManager()->Hold(this),
+ ClientManager()->Hold(aChild),
+ aAfter ? ClientManager()->Hold(aAfter) : nullptr);
+ return true;
+ }
+
+ virtual bool RemoveChild(Layer* aChild) override
+ {
+ if (!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ // hold on to aChild before we remove it!
+ ShadowableLayer *heldChild = ClientManager()->Hold(aChild);
+ if (!ContainerLayer::RemoveChild(aChild)) {
+ return false;
+ }
+ ClientManager()->AsShadowForwarder()->RemoveChild(ClientManager()->Hold(this), heldChild);
+ return true;
+ }
+
+ virtual bool RepositionChild(Layer* aChild, Layer* aAfter) override
+ {
+ if (!ClientManager()->InConstruction()) {
+ NS_ERROR("Can only set properties in construction phase");
+ return false;
+ }
+ if (!ContainerLayer::RepositionChild(aChild, aAfter)) {
+ return false;
+ }
+ ClientManager()->AsShadowForwarder()->RepositionChild(ClientManager()->Hold(this),
+ ClientManager()->Hold(aChild),
+ aAfter ? ClientManager()->Hold(aAfter) : nullptr);
+ return true;
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+ void ForceIntermediateSurface() { mUseIntermediateSurface = true; }
+
+ void SetSupportsComponentAlphaChildren(bool aSupports) { mSupportsComponentAlphaChildren = aSupports; }
+
+ virtual void Disconnect() override
+ {
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+class ClientRefLayer : public RefLayer,
+ public ClientLayer {
+public:
+ explicit ClientRefLayer(ClientLayerManager* aManager) :
+ RefLayer(aManager, static_cast<ClientLayer*>(this))
+ {
+ MOZ_COUNT_CTOR(ClientRefLayer);
+ }
+
+protected:
+ virtual ~ClientRefLayer()
+ {
+ MOZ_COUNT_DTOR(ClientRefLayer);
+ }
+
+public:
+ virtual Layer* AsLayer() { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() { return this; }
+
+ virtual void Disconnect()
+ {
+ ClientLayer::Disconnect();
+ }
+
+ virtual void RenderLayer() { }
+
+ virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface)
+ {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+
+private:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientImageLayer.cpp b/gfx/layers/client/ClientImageLayer.cpp
new file mode 100644
index 000000000..8703f77a5
--- /dev/null
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -0,0 +1,170 @@
+/* -*- 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" // for ClientLayerManager, etc
+#include "ImageContainer.h" // for AutoLockImage, etc
+#include "ImageLayers.h" // for ImageLayer
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/ImageClient.h" // for ImageClient, etc
+#include "mozilla/layers/LayersMessages.h" // for ImageLayerAttributes, etc
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class ClientImageLayer : public ImageLayer,
+ public ClientLayer {
+public:
+ explicit ClientImageLayer(ClientLayerManager* aLayerManager)
+ : ImageLayer(aLayerManager, static_cast<ClientLayer*>(this))
+ , mImageClientTypeContainer(CompositableType::UNKNOWN)
+ {
+ MOZ_COUNT_CTOR(ClientImageLayer);
+ }
+
+protected:
+ virtual ~ClientImageLayer()
+ {
+ DestroyBackBuffer();
+ MOZ_COUNT_DTOR(ClientImageLayer);
+ }
+
+ virtual void SetContainer(ImageContainer* aContainer) override
+ {
+ ImageLayer::SetContainer(aContainer);
+ mImageClientTypeContainer = CompositableType::UNKNOWN;
+ }
+
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ ImageLayer::SetVisibleRegion(aRegion);
+ }
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override
+ {
+ DestroyBackBuffer();
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mImageClient) {
+ mImageClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = ImageLayerAttributes(mSamplingFilter, mScaleToSize, mScaleMode);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ DestroyBackBuffer();
+ ClientLayer::Disconnect();
+ }
+
+ void DestroyBackBuffer()
+ {
+ if (mImageClient) {
+ mImageClient->SetLayer(nullptr);
+ mImageClient->OnDetach();
+ mImageClient = nullptr;
+ }
+ }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mImageClient;
+ }
+
+protected:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ CompositableType GetImageClientType()
+ {
+ if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
+ return mImageClientTypeContainer;
+ }
+
+ if (mContainer->IsAsync()) {
+ mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE;
+ return mImageClientTypeContainer;
+ }
+
+ AutoLockImage autoLock(mContainer);
+
+ mImageClientTypeContainer = autoLock.HasImage()
+ ? CompositableType::IMAGE : CompositableType::UNKNOWN;
+ return mImageClientTypeContainer;
+ }
+
+ RefPtr<ImageClient> mImageClient;
+ CompositableType mImageClientTypeContainer;
+};
+
+void
+ClientImageLayer::RenderLayer()
+{
+ RenderMaskLayers(this);
+
+ if (!mContainer) {
+ return;
+ }
+
+ if (!mImageClient ||
+ !mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+ CompositableType type = GetImageClientType();
+ if (type == CompositableType::UNKNOWN) {
+ return;
+ }
+ TextureFlags flags = TextureFlags::DEFAULT;
+ mImageClient = ImageClient::CreateImageClient(type,
+ ClientManager()->AsShadowForwarder(),
+ flags);
+ if (!mImageClient) {
+ return;
+ }
+ mImageClient->SetLayer(this);
+ if (HasShadow() && !mContainer->IsAsync()) {
+ mImageClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mImageClient, this);
+ }
+ if (!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+ return;
+ }
+ }
+ ClientManager()->Hold(this);
+}
+
+already_AddRefed<ImageLayer>
+ClientLayerManager::CreateImageLayer()
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ RefPtr<ClientImageLayer> layer =
+ new ClientImageLayer(this);
+ CREATE_SHADOW(Image);
+ return layer.forget();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp
new file mode 100644
index 000000000..074807e8c
--- /dev/null
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -0,0 +1,907 @@
+/* -*- 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 "GeckoProfiler.h" // for PROFILER_LABEL
+#include "gfxPrefs.h" // for gfxPrefs::LayersTile...
+#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"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/ContentClient.h"
+#include "mozilla/layers/FrameUniformityData.h"
+#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 "mozilla/layers/ShadowLayerChild.h"
+#include "mozilla/layers/PersistentBufferProvider.h"
+#include "ClientReadbackLayer.h" // for ClientReadbackLayer
+#include "nsAString.h"
+#include "nsDisplayList.h"
+#include "nsIWidgetListener.h"
+#include "nsTArray.h" // for AutoTArray
+#include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
+#include "TiledLayerBuffer.h"
+#include "mozilla/dom/WindowBinding.h" // for Overfill Callback
+#include "FrameLayerBuilder.h" // for FrameLayerbuilder
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "LayerMetricsWrapper.h"
+#endif
+#ifdef XP_WIN
+#include "mozilla/gfx/DeviceManagerDx.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ClientLayerManager::MemoryPressureObserver::Destroy()
+{
+ UnregisterMemoryPressureEvent();
+ mClientLayerManager = nullptr;
+}
+
+NS_IMETHODIMP
+ClientLayerManager::MemoryPressureObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aSomeData)
+{
+ if (!mClientLayerManager || strcmp(aTopic, "memory-pressure")) {
+ return NS_OK;
+ }
+
+ mClientLayerManager->HandleMemoryPressure();
+ return NS_OK;
+}
+
+void
+ClientLayerManager::MemoryPressureObserver::RegisterMemoryPressureEvent()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ MOZ_ASSERT(observerService);
+
+ if (observerService) {
+ observerService->AddObserver(this, "memory-pressure", false);
+ }
+}
+
+void
+ClientLayerManager::MemoryPressureObserver::UnregisterMemoryPressureEvent()
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService) {
+ observerService->RemoveObserver(this, "memory-pressure");
+ }
+}
+
+NS_IMPL_ISUPPORTS(ClientLayerManager::MemoryPressureObserver, nsIObserver)
+
+ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
+ : mPhase(PHASE_NONE)
+ , mWidget(aWidget)
+ , mLatestTransactionId(0)
+ , mLastPaintTime(TimeDuration::Forever())
+ , mTargetRotation(ROTATION_0)
+ , mRepeatTransaction(false)
+ , mIsRepeatTransaction(false)
+ , mTransactionIncomplete(false)
+ , mCompositorMightResample(false)
+ , mNeedsComposite(false)
+ , mPaintSequenceNumber(0)
+ , mForwarder(new ShadowLayerForwarder(this))
+ , mDeviceCounter(gfxPlatform::GetPlatform()->GetDeviceCounter())
+{
+ MOZ_COUNT_CTOR(ClientLayerManager);
+ mMemoryPressureObserver = new MemoryPressureObserver(this);
+}
+
+
+ClientLayerManager::~ClientLayerManager()
+{
+ mMemoryPressureObserver->Destroy();
+ ClearCachedResources();
+ // Stop receiveing AsyncParentMessage at Forwarder.
+ // After the call, the message is directly handled by LayerTransactionChild.
+ // Basically this function should be called in ShadowLayerForwarder's
+ // destructor. But when the destructor is triggered by
+ // CompositorBridgeChild::Destroy(), the destructor can not handle it correctly.
+ // See Bug 1000525.
+ mForwarder->StopReceiveAsyncParentMessge();
+ mRoot = nullptr;
+
+ MOZ_COUNT_DTOR(ClientLayerManager);
+}
+
+void
+ClientLayerManager::Destroy()
+{
+ // It's important to call ClearCachedResource before Destroy because the
+ // former will early-return if the later has already run.
+ ClearCachedResources();
+ LayerManager::Destroy();
+
+ if (mTransactionIdAllocator) {
+ // Make sure to notify the refresh driver just in case it's waiting on a
+ // pending transaction. Do this at the top of the event loop so we don't
+ // cause a paint to occur during compositor shutdown.
+ RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
+ uint64_t id = mLatestTransactionId;
+
+ RefPtr<Runnable> task = NS_NewRunnableFunction([allocator, id] () -> void {
+ allocator->NotifyTransactionCompleted(id);
+ });
+ NS_DispatchToMainThread(task.forget());
+ }
+
+ // Forget the widget pointer in case we outlive our owning widget.
+ mWidget = nullptr;
+}
+
+int32_t
+ClientLayerManager::GetMaxTextureSize() const
+{
+ return mForwarder->GetMaxTextureSize();
+}
+
+void
+ClientLayerManager::SetDefaultTargetConfiguration(BufferMode aDoubleBuffering,
+ ScreenRotation aRotation)
+{
+ mTargetRotation = aRotation;
+ }
+
+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));
+}
+
+already_AddRefed<ReadbackLayer>
+ClientLayerManager::CreateReadbackLayer()
+{
+ RefPtr<ReadbackLayer> layer = new ClientReadbackLayer(this);
+ return layer.forget();
+}
+
+bool
+ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+ MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
+ if (!mForwarder->IPCOpen()) {
+ gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
+ return false;
+ }
+
+ mInTransaction = true;
+ mTransactionStart = TimeStamp::Now();
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ MOZ_LAYERS_LOG(("[----- BeginTransaction"));
+ Log();
+#endif
+
+ NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
+ mPhase = PHASE_CONSTRUCTION;
+
+ if (DependsOnStaleDevice()) {
+ FrameLayerBuilder::InvalidateAllLayers(this);
+ mDeviceCounter = gfxPlatform::GetPlatform()->GetDeviceCounter();
+ }
+
+ MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?");
+
+ // If the last transaction was incomplete (a failed DoEmptyTransaction),
+ // don't signal a new transaction to ShadowLayerForwarder. Carry on adding
+ // to the previous transaction.
+ dom::ScreenOrientationInternal orientation;
+ if (dom::TabChild* window = mWidget->GetOwningTabChild()) {
+ orientation = window->GetOrientation();
+ } else {
+ hal::ScreenConfiguration currentConfig;
+ hal::GetCurrentScreenConfiguration(&currentConfig);
+ orientation = currentConfig.orientation();
+ }
+ LayoutDeviceIntRect targetBounds = mWidget->GetNaturalBounds();
+ targetBounds.x = targetBounds.y = 0;
+ mForwarder->BeginTransaction(targetBounds.ToUnknownRect(), mTargetRotation,
+ orientation);
+
+ // If we're drawing on behalf of a context with async pan/zoom
+ // enabled, then the entire buffer of painted 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.
+ //
+ // Desktop does not support async zoom yet, so we ignore this for those
+ // platforms.
+#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
+ if (mWidget && mWidget->GetOwningTabChild()) {
+ mCompositorMightResample = AsyncPanZoomEnabled();
+ }
+#endif
+
+ // 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_IsParentProcess()) {
+ mShadowTarget = aTarget;
+ } else {
+ NS_ASSERTION(!aTarget,
+ "Content-process ClientLayerManager::BeginTransactionWithTarget not supported");
+ }
+
+ // If this is a new paint, increment the paint sequence number.
+ if (!mIsRepeatTransaction) {
+ // Increment the paint sequence number even if test logging isn't
+ // enabled in this process; it may be enabled in the parent process,
+ // and the parent process expects unique sequence numbers.
+ ++mPaintSequenceNumber;
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ mApzTestData.StartNewPaint(mPaintSequenceNumber);
+ }
+ }
+ return true;
+}
+
+bool
+ClientLayerManager::BeginTransaction()
+{
+ return BeginTransactionWithTarget(nullptr);
+}
+
+bool
+ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags)
+{
+ PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Rasterization);
+
+ PROFILER_LABEL("ClientLayerManager", "EndTransactionInternal",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#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();
+
+ mPaintedLayerCallback = aCallback;
+ mPaintedLayerCallbackData = aCallbackData;
+
+ GetRoot()->ComputeEffectiveTransforms(Matrix4x4());
+
+ // Skip the painting if the device is in device-reset status.
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+ TimeStamp start = TimeStamp::Now();
+ root->RenderLayer();
+ mLastPaintTime = TimeStamp::Now() - start;
+ } else {
+ root->RenderLayer();
+ }
+ } else {
+ gfxCriticalNote << "LayerManager::EndTransaction skip RenderLayer().";
+ }
+
+ if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) {
+ GetRoot()->Mutated();
+ }
+
+ if (!mIsRepeatTransaction) {
+ mAnimationReadyTime = TimeStamp::Now();
+ GetRoot()->StartPendingAnimations(mAnimationReadyTime);
+ }
+
+ mPaintedLayerCallback = nullptr;
+ mPaintedLayerCallbackData = 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");
+
+ if (gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ FrameLayerBuilder::InvalidateAllLayers(this);
+ }
+
+ return !mTransactionIncomplete;
+}
+
+void
+ClientLayerManager::StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>& aConfigurations)
+{
+ if (mForwarder) {
+ mForwarder->StorePluginWidgetConfigurations(aConfigurations);
+ }
+}
+
+void
+ClientLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags)
+{
+ if (!mForwarder->IPCOpen()) {
+ mInTransaction = false;
+ return;
+ }
+
+ if (mWidget) {
+ mWidget->PrepareWindowEffects();
+ }
+ EndTransactionInternal(aCallback, aCallbackData, aFlags);
+ ForwardTransaction(!(aFlags & END_NO_REMOTE_COMPOSITE));
+
+ if (mRepeatTransaction) {
+ mRepeatTransaction = false;
+ mIsRepeatTransaction = true;
+ if (BeginTransaction()) {
+ ClientLayerManager::EndTransaction(aCallback, aCallbackData, aFlags);
+ }
+ mIsRepeatTransaction = false;
+ } else {
+ MakeSnapshotIfRequired();
+ }
+
+ mInTransaction = false;
+ mTransactionStart = TimeStamp();
+}
+
+bool
+ClientLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+ mInTransaction = false;
+
+ if (!mRoot || !mForwarder->IPCOpen()) {
+ 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;
+}
+
+CompositorBridgeChild *
+ClientLayerManager::GetRemoteRenderer()
+{
+ if (!mWidget) {
+ return nullptr;
+ }
+
+ return mWidget->GetRemoteRenderer();
+}
+
+CompositorBridgeChild*
+ClientLayerManager::GetCompositorBridgeChild()
+{
+ if (!XRE_IsParentProcess()) {
+ return CompositorBridgeChild::Get();
+ }
+ return GetRemoteRenderer();
+}
+
+void
+ClientLayerManager::Composite()
+{
+ mForwarder->Composite();
+}
+
+void
+ClientLayerManager::DidComposite(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd)
+{
+ MOZ_ASSERT(mWidget);
+
+ // |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
+ // layers transaction.
+ if (aTransactionId) {
+ nsIWidgetListener *listener = mWidget->GetWidgetListener();
+ if (listener) {
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ listener = mWidget->GetAttachedWidgetListener();
+ if (listener) {
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+ mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
+ }
+
+ // These observers fire whether or not we were in a transaction.
+ for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
+ mDidCompositeObservers[i]->DidComposite();
+ }
+}
+
+void
+ClientLayerManager::GetCompositorSideAPZTestData(APZTestData* aData) const
+{
+ if (mForwarder->HasShadowManager()) {
+ if (!mForwarder->GetShadowManager()->SendGetAPZTestData(aData)) {
+ NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
+ }
+ }
+}
+
+float
+ClientLayerManager::RequestProperty(const nsAString& aProperty)
+{
+ if (mForwarder->HasShadowManager()) {
+ float value;
+ if (!mForwarder->GetShadowManager()->SendRequestProperty(nsString(aProperty), &value)) {
+ NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
+ }
+ return value;
+ }
+ return -1;
+}
+
+void
+ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber)
+{
+ if (gfxPrefs::APZTestLoggingEnabled()) {
+ mApzTestData.StartNewRepaintRequest(aSequenceNumber);
+ }
+}
+
+void
+ClientLayerManager::GetFrameUniformity(FrameUniformityData* aOutData)
+{
+ MOZ_ASSERT(XRE_IsParentProcess(), "Frame Uniformity only supported in parent process");
+
+ if (HasShadowManager()) {
+ CompositorBridgeChild* child = GetRemoteRenderer();
+ child->SendGetFrameUniformity(aOutData);
+ return;
+ }
+
+ return LayerManager::GetFrameUniformity(aOutData);
+}
+
+bool
+ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback)
+{
+ MOZ_ASSERT(aCallback != nullptr);
+ MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now");
+
+ if (HasShadowManager()) {
+ CompositorBridgeChild* child = GetRemoteRenderer();
+ NS_ASSERTION(child, "Could not get CompositorBridgeChild");
+
+ child->AddOverfillObserver(this);
+ child->SendRequestOverfill();
+ mOverfillCallbacks.AppendElement(aCallback);
+ }
+
+ return true;
+}
+
+void
+ClientLayerManager::RunOverfillCallback(const uint32_t aOverfill)
+{
+ for (size_t i = 0; i < mOverfillCallbacks.Length(); i++) {
+ ErrorResult error;
+ mOverfillCallbacks[i]->Call(aOverfill, error);
+ }
+
+ mOverfillCallbacks.Clear();
+}
+
+void
+ClientLayerManager::MakeSnapshotIfRequired()
+{
+ if (!mShadowTarget) {
+ return;
+ }
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) {
+ // The compositor doesn't draw to a different sized surface
+ // when there's a rotation. Instead we rotate the result
+ // when drawing into dt
+ LayoutDeviceIntRect outerBounds = mWidget->GetBounds();
+
+ IntRect bounds = ToOutsideIntRect(mShadowTarget->GetClipExtents());
+ if (mTargetRotation) {
+ bounds =
+ RotateRect(bounds, outerBounds.ToUnknownRect(), mTargetRotation);
+ }
+
+ SurfaceDescriptor inSnapshot;
+ if (!bounds.IsEmpty() &&
+ mForwarder->AllocSurfaceDescriptor(bounds.Size(),
+ gfxContentType::COLOR_ALPHA,
+ &inSnapshot)) {
+
+ // Make a copy of |inSnapshot| because the call to send it over IPC
+ // will call forget() on the Shmem inside, and zero it out.
+ SurfaceDescriptor outSnapshot = inSnapshot;
+
+ if (remoteRenderer->SendMakeSnapshot(inSnapshot, bounds)) {
+ RefPtr<DataSourceSurface> surf = GetSurfaceForDescriptor(outSnapshot);
+ DrawTarget* dt = mShadowTarget->GetDrawTarget();
+
+ Rect dstRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ Rect srcRect(0, 0, bounds.width, bounds.height);
+
+ gfx::Matrix rotate =
+ ComputeTransformForUnRotation(outerBounds.ToUnknownRect(),
+ mTargetRotation);
+
+ gfx::Matrix oldMatrix = dt->GetTransform();
+ dt->SetTransform(rotate * oldMatrix);
+ dt->DrawSurface(surf, dstRect, srcRect,
+ DrawSurfaceOptions(),
+ DrawOptions(1.0f, CompositionOp::OP_OVER));
+ dt->SetTransform(oldMatrix);
+ }
+ mForwarder->DestroySurfaceDescriptor(&outSnapshot);
+ }
+ }
+ }
+ mShadowTarget = nullptr;
+}
+
+void
+ClientLayerManager::FlushRendering()
+{
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+ remoteRenderer->SendFlushRendering();
+ }
+ }
+}
+
+void
+ClientLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier)
+{
+ mForwarder->IdentifyTextureHost(aNewIdentifier);
+}
+
+void
+ClientLayerManager::SendInvalidRegion(const nsIntRegion& aRegion)
+{
+ if (mWidget) {
+ if (CompositorBridgeChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+ remoteRenderer->SendNotifyRegionInvalidated(aRegion);
+ }
+ }
+}
+
+uint32_t
+ClientLayerManager::StartFrameTimeRecording(int32_t aBufferSize)
+{
+ CompositorBridgeChild* renderer = GetRemoteRenderer();
+ if (renderer) {
+ uint32_t startIndex;
+ renderer->SendStartFrameTimeRecording(aBufferSize, &startIndex);
+ return startIndex;
+ }
+ return -1;
+}
+
+void
+ClientLayerManager::StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals)
+{
+ CompositorBridgeChild* renderer = GetRemoteRenderer();
+ if (renderer) {
+ renderer->SendStopFrameTimeRecording(aStartIndex, &aFrameIntervals);
+ }
+}
+
+void
+ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
+{
+ TimeStamp start = TimeStamp::Now();
+
+ // Skip the synchronization for buffer since we also skip the painting during
+ // device-reset status.
+ if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+ if (mForwarder->GetSyncObject() &&
+ mForwarder->GetSyncObject()->IsSyncObjectValid()) {
+ mForwarder->GetSyncObject()->FinalizeFrame();
+ }
+ }
+
+ mPhase = PHASE_FORWARD;
+
+ mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
+ TimeStamp transactionStart;
+ if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
+ transactionStart = mTransactionIdAllocator->GetTransactionStart();
+ } else {
+ transactionStart = mTransactionStart;
+ }
+
+ if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+ mForwarder->SendPaintTime(mLatestTransactionId, mLastPaintTime);
+ }
+
+ // forward this transaction's changeset to our LayerManagerComposite
+ bool sent;
+ AutoTArray<EditReply, 10> replies;
+ if (mForwarder->EndTransaction(&replies, mRegionToClear,
+ mLatestTransactionId, aScheduleComposite, mPaintSequenceNumber,
+ mIsRepeatTransaction, transactionStart, &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();
+
+ RefPtr<CompositableClient> compositable =
+ CompositableClient::FromIPDLActor(obs.compositableChild());
+ ContentClientRemote* contentClient =
+ static_cast<ContentClientRemote*>(compositable.get());
+ MOZ_ASSERT(contentClient);
+
+ contentClient->SwapBuffers(obs.frontUpdatedRegion());
+
+ break;
+ }
+ default:
+ NS_RUNTIMEABORT("not reached");
+ }
+ }
+
+ if (sent) {
+ mNeedsComposite = false;
+ }
+ } else if (HasShadowManager()) {
+ NS_WARNING("failed to forward Layers transaction");
+ }
+
+ if (!sent) {
+ // Clear the transaction id so that it doesn't get returned
+ // unless we forwarded to somewhere that doesn't actually
+ // have a compositor.
+ mTransactionIdAllocator->RevokeTransactionId(mLatestTransactionId);
+ }
+
+ mPhase = PHASE_NONE;
+
+ // this may result in Layers being deleted, which results in
+ // PLayer::Send__delete__() and DeallocShmem()
+ mKeepAlive.Clear();
+
+ TabChild* window = mWidget ? mWidget->GetOwningTabChild() : nullptr;
+ if (window) {
+ TimeStamp end = TimeStamp::Now();
+ window->DidRequestComposite(start, end);
+ }
+}
+
+ShadowableLayer*
+ClientLayerManager::Hold(Layer* aLayer)
+{
+ MOZ_ASSERT(HasShadowManager(),
+ "top-level tree, no shadow tree to remote to");
+
+ ShadowableLayer* shadowable = ClientLayer::ToClientLayer(aLayer);
+ MOZ_ASSERT(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());
+}
+
+bool
+ClientLayerManager::AreComponentAlphaLayersEnabled()
+{
+ return GetCompositorBackendType() != LayersBackend::LAYERS_BASIC &&
+ AsShadowForwarder()->SupportsComponentAlpha() &&
+ LayerManager::AreComponentAlphaLayersEnabled();
+}
+
+void
+ClientLayerManager::SetIsFirstPaint()
+{
+ mForwarder->SetIsFirstPaint();
+}
+
+void
+ClientLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+ if (mDestroyed) {
+ // ClearCachedResource was already called by ClientLayerManager::Destroy
+ return;
+ }
+ MOZ_ASSERT(!HasShadowManager() || !aSubtree);
+ mForwarder->ClearCachedResources();
+ if (aSubtree) {
+ ClearLayer(aSubtree);
+ } else if (mRoot) {
+ ClearLayer(mRoot);
+ }
+}
+
+void
+ClientLayerManager::HandleMemoryPressure()
+{
+ if (mRoot) {
+ HandleMemoryPressureLayer(mRoot);
+ }
+
+ if (GetCompositorBridgeChild()) {
+ GetCompositorBridgeChild()->HandleMemoryPressure();
+ }
+}
+
+void
+ClientLayerManager::ClearLayer(Layer* aLayer)
+{
+ ClientLayer::ToClientLayer(aLayer)->ClearCachedResources();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ ClearLayer(child);
+ }
+}
+
+void
+ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer)
+{
+ ClientLayer::ToClientLayer(aLayer)->HandleMemoryPressure();
+ for (Layer* child = aLayer->GetFirstChild(); child;
+ child = child->GetNextSibling()) {
+ HandleMemoryPressureLayer(child);
+ }
+}
+
+void
+ClientLayerManager::GetBackendName(nsAString& aName)
+{
+ switch (mForwarder->GetCompositorBackendType()) {
+ case LayersBackend::LAYERS_NONE: aName.AssignLiteral("None"); return;
+ case LayersBackend::LAYERS_BASIC: aName.AssignLiteral("Basic"); return;
+ case LayersBackend::LAYERS_OPENGL: aName.AssignLiteral("OpenGL"); return;
+ case LayersBackend::LAYERS_D3D9: aName.AssignLiteral("Direct3D 9"); return;
+ case LayersBackend::LAYERS_D3D11: {
+#ifdef XP_WIN
+ if (DeviceManagerDx::Get()->IsWARP()) {
+ aName.AssignLiteral("Direct3D 11 WARP");
+ } else {
+ aName.AssignLiteral("Direct3D 11");
+ }
+#endif
+ return;
+ }
+ default: NS_RUNTIMEABORT("Invalid backend");
+ }
+}
+
+bool
+ClientLayerManager::AsyncPanZoomEnabled() const
+{
+ return mWidget && mWidget->AsyncPanZoomEnabled();
+}
+
+void
+ClientLayerManager::SetNextPaintSyncId(int32_t aSyncId)
+{
+ mForwarder->SetPaintSyncId(aSyncId);
+}
+
+void
+ClientLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+ mForwarder->SetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+void
+ClientLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+ if (!mDidCompositeObservers.Contains(aObserver)) {
+ mDidCompositeObservers.AppendElement(aObserver);
+ }
+}
+
+void
+ClientLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+ mDidCompositeObservers.RemoveElement(aObserver);
+}
+
+bool
+ClientLayerManager::DependsOnStaleDevice() const
+{
+ return gfxPlatform::GetPlatform()->GetDeviceCounter() != mDeviceCounter;
+}
+
+
+already_AddRefed<PersistentBufferProvider>
+ClientLayerManager::CreatePersistentBufferProvider(const gfx::IntSize& aSize,
+ gfx::SurfaceFormat aFormat)
+{
+ // Don't use a shared buffer provider if compositing is considered "not cheap"
+ // because the canvas will most likely be flattened into a thebes layer instead
+ // of being sent to the compositor, in which case rendering into shared memory
+ // is wasteful.
+ if (IsCompositingCheap() &&
+ gfxPrefs::PersistentBufferProviderSharedEnabled()) {
+ RefPtr<PersistentBufferProvider> provider
+ = PersistentBufferProviderShared::Create(aSize, aFormat, AsShadowForwarder());
+ if (provider) {
+ return provider.forget();
+ }
+ }
+
+ return LayerManager::CreatePersistentBufferProvider(aSize, aFormat);
+}
+
+
+ClientLayer::~ClientLayer()
+{
+ if (HasShadow()) {
+ PLayerChild::Send__delete__(GetShadow());
+ }
+ MOZ_COUNT_DTOR(ClientLayer);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientLayerManager.h b/gfx/layers/client/ClientLayerManager.h
new file mode 100644
index 000000000..5bcd5e412
--- /dev/null
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -0,0 +1,422 @@
+/* -*- 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/. */
+
+#ifndef GFX_CLIENTLAYERMANAGER_H
+#define GFX_CLIENTLAYERMANAGER_H
+
+#include <stdint.h> // for int32_t
+#include "Layers.h"
+#include "gfxContext.h" // for gfxContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/LinkedList.h" // For LinkedList
+#include "mozilla/WidgetUtils.h" // for ScreenRotation
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for BufferMode, LayersBackend, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder, etc
+#include "mozilla/layers/APZTestData.h" // for APZTestData
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsIObserver.h" // for nsIObserver
+#include "nsISupportsImpl.h" // for Layer::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsTArray.h" // for nsTArray
+#include "nscore.h" // for nsAString
+#include "mozilla/layers/TransactionIdAllocator.h"
+#include "nsIWidget.h" // For plugin window configuration information structs
+
+namespace mozilla {
+namespace layers {
+
+class ClientPaintedLayer;
+class CompositorBridgeChild;
+class ImageLayer;
+class PLayerChild;
+class FrameUniformityData;
+
+class ClientLayerManager final : public LayerManager
+{
+ typedef nsTArray<RefPtr<Layer> > LayerRefArray;
+
+public:
+ explicit ClientLayerManager(nsIWidget* aWidget);
+
+ virtual void Destroy() override;
+
+protected:
+ virtual ~ClientLayerManager();
+
+public:
+ virtual ShadowLayerForwarder* AsShadowForwarder() override
+ {
+ return mForwarder;
+ }
+
+ virtual ClientLayerManager* AsClientLayerManager() override
+ {
+ return this;
+ }
+
+ virtual int32_t GetMaxTextureSize() const override;
+
+ virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+ virtual bool BeginTransaction() override;
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) override;
+
+ virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_CLIENT; }
+ virtual LayersBackend GetCompositorBackendType() override
+ {
+ return AsShadowForwarder()->GetCompositorBackendType();
+ }
+ virtual void GetBackendName(nsAString& name) override;
+ virtual const char* Name() const override { return "Client"; }
+
+ virtual void SetRoot(Layer* aLayer) override;
+
+ virtual void Mutated(Layer* aLayer) override;
+
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint) override;
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+ virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+ virtual already_AddRefed<RefLayer> CreateRefLayer() override;
+
+ void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier);
+ TextureFactoryIdentifier GetTextureFactoryIdentifier()
+ {
+ return AsShadowForwarder()->GetTextureFactoryIdentifier();
+ }
+
+ virtual void FlushRendering() override;
+ void SendInvalidRegion(const nsIntRegion& aRegion);
+
+ virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize) override;
+
+ virtual void StopFrameTimeRecording(uint32_t aStartIndex,
+ nsTArray<float>& aFrameIntervals) override;
+
+ virtual bool NeedsWidgetInvalidation() override { return false; }
+
+ ShadowableLayer* Hold(Layer* aLayer);
+
+ bool HasShadowManager() const { return mForwarder->HasShadowManager(); }
+
+ virtual bool IsCompositingCheap() override;
+ virtual bool HasShadowManagerInternal() const override { return HasShadowManager(); }
+
+ virtual void SetIsFirstPaint() override;
+
+ /**
+ * Pass through call to the forwarder for nsPresContext's
+ * CollectPluginGeometryUpdates. Passes widget configuration information
+ * to the compositor for transmission to the chrome process. This
+ * configuration gets set when the window paints.
+ */
+ void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
+ aConfigurations) override;
+
+ // Drop cached resources and ask our shadow manager to do the same,
+ // if we have one.
+ virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+
+ void HandleMemoryPressure();
+
+ void SetRepeatTransaction() { mRepeatTransaction = true; }
+ bool GetRepeatTransaction() { return mRepeatTransaction; }
+
+ bool IsRepeatTransaction() { return mIsRepeatTransaction; }
+
+ void SetTransactionIncomplete() { mTransactionIncomplete = true; }
+
+ bool HasShadowTarget() { return !!mShadowTarget; }
+
+ void SetShadowTarget(gfxContext* aTarget) { mShadowTarget = aTarget; }
+
+ bool CompositorMightResample() { return mCompositorMightResample; }
+
+ DrawPaintedLayerCallback GetPaintedLayerCallback() const
+ { return mPaintedLayerCallback; }
+
+ void* GetPaintedLayerCallbackData() const
+ { return mPaintedLayerCallbackData; }
+
+ CompositorBridgeChild* GetRemoteRenderer();
+
+ CompositorBridgeChild* GetCompositorBridgeChild();
+
+ // Disable component alpha layers with the software compositor.
+ virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); }
+
+ bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
+#ifdef DEBUG
+ bool InDrawing() { return mPhase == PHASE_DRAWING; }
+ bool InForward() { return mPhase == PHASE_FORWARD; }
+#endif
+ bool InTransaction() { return mPhase != PHASE_NONE; }
+
+ void SetNeedsComposite(bool aNeedsComposite)
+ {
+ mNeedsComposite = aNeedsComposite;
+ }
+ bool NeedsComposite() const { return mNeedsComposite; }
+
+ virtual void Composite() override;
+ virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
+ virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
+ virtual void RunOverfillCallback(const uint32_t aOverfill) override;
+
+ void DidComposite(uint64_t aTransactionId,
+ const mozilla::TimeStamp& aCompositeStart,
+ const mozilla::TimeStamp& aCompositeEnd);
+
+ virtual bool AreComponentAlphaLayersEnabled() override;
+
+ // Log APZ test data for the current paint. We supply the paint sequence
+ // number ourselves, and take care of calling APZTestData::StartNewPaint()
+ // when a new paint is started.
+ void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue)
+ {
+ mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Log APZ test data for a repaint request. The sequence number must be
+ // passed in from outside, and APZTestData::StartNewRepaintRequest() needs
+ // to be called from the outside as well when a new repaint request is started.
+ void StartNewRepaintRequest(SequenceNumber aSequenceNumber);
+
+ // TODO(botond): When we start using this and write a wrapper similar to
+ // nsLayoutUtils::LogTestDataForPaint(), make sure that wrapper checks
+ // gfxPrefs::APZTestLoggingEnabled().
+ void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
+ FrameMetrics::ViewID aScrollId,
+ const std::string& aKey,
+ const std::string& aValue)
+ {
+ mApzTestData.LogTestDataForRepaintRequest(aSequenceNumber, aScrollId, aKey, aValue);
+ }
+
+ // Get the content-side APZ test data for reading. For writing, use the
+ // LogTestData...() functions.
+ const APZTestData& GetAPZTestData() const {
+ return mApzTestData;
+ }
+
+ // Get a copy of the compositor-side APZ test data for our layers ID.
+ void GetCompositorSideAPZTestData(APZTestData* aData) const;
+
+ void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
+
+ float RequestProperty(const nsAString& aProperty) override;
+
+ bool AsyncPanZoomEnabled() const override;
+
+ void SetNextPaintSyncId(int32_t aSyncId);
+
+ void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+
+ class DidCompositeObserver {
+ public:
+ virtual void DidComposite() = 0;
+ };
+
+ void AddDidCompositeObserver(DidCompositeObserver* aObserver);
+ void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
+
+ virtual already_AddRefed<PersistentBufferProvider>
+ CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
+
+protected:
+ enum TransactionPhase {
+ PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
+ };
+ TransactionPhase mPhase;
+
+private:
+ // Listen memory-pressure event for ClientLayerManager
+ class MemoryPressureObserver final : public nsIObserver
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ explicit MemoryPressureObserver(ClientLayerManager* aClientLayerManager)
+ : mClientLayerManager(aClientLayerManager)
+ {
+ RegisterMemoryPressureEvent();
+ }
+
+ void Destroy();
+
+ private:
+ virtual ~MemoryPressureObserver() {}
+ void RegisterMemoryPressureEvent();
+ void UnregisterMemoryPressureEvent();
+
+ ClientLayerManager* mClientLayerManager;
+ };
+
+ /**
+ * Forward transaction results to the parent context.
+ */
+ void ForwardTransaction(bool aScheduleComposite);
+
+ /**
+ * Take a snapshot of the parent context, and copy
+ * it into mShadowTarget.
+ */
+ void MakeSnapshotIfRequired();
+
+ void ClearLayer(Layer* aLayer);
+
+ void HandleMemoryPressureLayer(Layer* aLayer);
+
+ bool EndTransactionInternal(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags);
+
+ bool DependsOnStaleDevice() const;
+
+ LayerRefArray mKeepAlive;
+
+ nsIWidget* mWidget;
+
+ /* PaintedLayer callbacks; valid at the end of a transaciton,
+ * while rendering */
+ DrawPaintedLayerCallback mPaintedLayerCallback;
+ void *mPaintedLayerCallbackData;
+
+ // When we're doing a transaction in order to draw to a non-default
+ // target, the layers transaction is only performed in order to send
+ // a PLayers:Update. We save the original non-default target to
+ // mShadowTarget, and then perform the transaction using
+ // mDummyTarget as the render target. After the transaction ends,
+ // we send a message to our remote side to capture the actual pixels
+ // being drawn to the default target, and then copy those pixels
+ // back to mShadowTarget.
+ RefPtr<gfxContext> mShadowTarget;
+
+ RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
+ uint64_t mLatestTransactionId;
+ TimeDuration mLastPaintTime;
+
+ // Sometimes we draw to targets that don't natively support
+ // landscape/portrait orientation. When we need to implement that
+ // ourselves, |mTargetRotation| describes the induced transform we
+ // need to apply when compositing content to our target.
+ ScreenRotation mTargetRotation;
+
+ // Used to repeat the transaction right away (to avoid rebuilding
+ // a display list) to support progressive drawing.
+ bool mRepeatTransaction;
+ bool mIsRepeatTransaction;
+ bool mTransactionIncomplete;
+ bool mCompositorMightResample;
+ bool mNeedsComposite;
+
+ // An incrementing sequence number for paints.
+ // Incremented in BeginTransaction(), but not for repeat transactions.
+ uint32_t mPaintSequenceNumber;
+
+ APZTestData mApzTestData;
+
+ RefPtr<ShadowLayerForwarder> mForwarder;
+ AutoTArray<dom::OverfillCallback*,0> mOverfillCallbacks;
+ mozilla::TimeStamp mTransactionStart;
+
+ nsTArray<DidCompositeObserver*> mDidCompositeObservers;
+
+ RefPtr<MemoryPressureObserver> mMemoryPressureObserver;
+ uint64_t mDeviceCounter;
+};
+
+class ClientLayer : public ShadowableLayer
+{
+public:
+ ClientLayer()
+ {
+ MOZ_COUNT_CTOR(ClientLayer);
+ }
+
+ ~ClientLayer();
+
+ void SetShadow(PLayerChild* aShadow)
+ {
+ MOZ_ASSERT(!mShadow, "can't have two shadows (yet)");
+ mShadow = aShadow;
+ }
+
+ virtual void Disconnect()
+ {
+ // This is an "emergency Disconnect()", called when the compositing
+ // process has died. |mShadow| and our Shmem buffers are
+ // automatically managed by IPDL, so we don't need to explicitly
+ // free them here (it's hard to get that right on emergency
+ // shutdown anyway).
+ mShadow = nullptr;
+ }
+
+ virtual void ClearCachedResources() { }
+
+ // Shrink memory usage.
+ // Called when "memory-pressure" is observed.
+ virtual void HandleMemoryPressure() { }
+
+ virtual void RenderLayer() = 0;
+ virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); }
+
+ virtual ClientPaintedLayer* AsThebes() { return nullptr; }
+
+ static inline ClientLayer *
+ ToClientLayer(Layer* aLayer)
+ {
+ return static_cast<ClientLayer*>(aLayer->ImplData());
+ }
+
+ template <typename LayerType>
+ static inline void RenderMaskLayers(LayerType* aLayer) {
+ if (aLayer->GetMaskLayer()) {
+ ToClientLayer(aLayer->GetMaskLayer())->RenderLayer();
+ }
+ for (size_t i = 0; i < aLayer->GetAncestorMaskLayerCount(); i++) {
+ ToClientLayer(aLayer->GetAncestorMaskLayerAt(i))->RenderLayer();
+ }
+ }
+};
+
+// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
+// our layer tree to a parent process. Record the new layer creation
+// in the current open transaction as a side effect.
+template<typename CreatedMethod> void
+CreateShadowFor(ClientLayer* aLayer,
+ ClientLayerManager* aMgr,
+ CreatedMethod aMethod)
+{
+ PLayerChild* shadow = aMgr->AsShadowForwarder()->ConstructShadowFor(aLayer);
+ if (!shadow) {
+ return;
+ }
+
+ aLayer->SetShadow(shadow);
+ (aMgr->AsShadowForwarder()->*aMethod)(aLayer);
+ aMgr->Hold(aLayer->AsLayer());
+}
+
+#define CREATE_SHADOW(_type) \
+ CreateShadowFor(layer, this, \
+ &ShadowLayerForwarder::Created ## _type ## Layer)
+
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CLIENTLAYERMANAGER_H */
diff --git a/gfx/layers/client/ClientPaintedLayer.cpp b/gfx/layers/client/ClientPaintedLayer.cpp
new file mode 100644
index 000000000..871f10559
--- /dev/null
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -0,0 +1,175 @@
+/* -*- 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 "ClientPaintedLayer.h"
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include <stdint.h> // for uint32_t
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "client/ClientLayerManager.h" // for ClientLayerManager, etc
+#include "gfxContext.h" // for gfxContext
+#include "gfxRect.h" // for gfxRect
+#include "gfxPrefs.h" // for gfxPrefs
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Matrix.h" // for Matrix
+#include "mozilla/gfx/Rect.h" // for Rect, IntRect
+#include "mozilla/gfx/Types.h" // for Float, etc
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/Preferences.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for Layer::AddRef, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "gfx2DGlue.h"
+#include "ReadbackProcessor.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+ClientPaintedLayer::PaintThebes()
+{
+ PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ NS_ASSERTION(ClientManager()->InDrawing(),
+ "Can only draw in drawing phase");
+
+ uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ if (ClientManager()->CompositorMightResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ if (!(flags & RotatedContentBuffer::PAINT_WILL_RESAMPLE)) {
+ if (MayResample()) {
+ flags |= RotatedContentBuffer::PAINT_WILL_RESAMPLE;
+ }
+ }
+#endif
+ PaintState state =
+ mContentClient->BeginPaintBuffer(this, flags);
+ mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
+
+ if (!state.mRegionToDraw.IsEmpty() && !ClientManager()->GetPaintedLayerCallback()) {
+ ClientManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ // The area that became invalid and is visible needs to be repainted
+ // (this could be the whole visible area if our buffer switched
+ // from RGB to RGBA, because we might need to repaint with
+ // subpixel AA)
+ state.mRegionToInvalidate.And(state.mRegionToInvalidate,
+ GetLocalVisibleRegion().ToUnknownRegion());
+
+ bool didUpdate = false;
+ RotatedContentBuffer::DrawIterator iter;
+ while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
+ if (!target || !target->IsValid()) {
+ if (target) {
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ }
+ continue;
+ }
+
+ SetAntialiasingFlags(this, target);
+
+ RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
+ MOZ_ASSERT(ctx); // already checked the target above
+
+ ClientManager()->GetPaintedLayerCallback()(this,
+ ctx,
+ iter.mDrawRegion,
+ iter.mDrawRegion,
+ state.mClip,
+ state.mRegionToInvalidate,
+ ClientManager()->GetPaintedLayerCallbackData());
+
+ ctx = nullptr;
+ mContentClient->ReturnDrawTargetToBuffer(target);
+ didUpdate = true;
+ }
+
+ if (didUpdate) {
+ Mutated();
+
+ mValidRegion.Or(mValidRegion, state.mRegionToDraw);
+
+ ContentClientRemote* contentClientRemote = static_cast<ContentClientRemote*>(mContentClient.get());
+ MOZ_ASSERT(contentClientRemote->GetIPDLActor());
+
+ // Hold(this) ensures this layer is kept alive through the current transaction
+ // The ContentClient assumes this layer is kept alive (e.g., in CreateBuffer),
+ // so deleting this Hold for whatever reason will break things.
+ ClientManager()->Hold(this);
+ contentClientRemote->Updated(state.mRegionToDraw,
+ mVisibleRegion.ToUnknownRegion(),
+ state.mDidSelfCopy);
+ }
+}
+
+void
+ClientPaintedLayer::RenderLayerWithReadback(ReadbackProcessor *aReadback)
+{
+ RenderMaskLayers(this);
+
+ if (!mContentClient) {
+ mContentClient = ContentClient::CreateContentClient(ClientManager()->AsShadowForwarder());
+ if (!mContentClient) {
+ return;
+ }
+ mContentClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
+ MOZ_ASSERT(mContentClient->GetForwarder());
+ }
+
+ nsTArray<ReadbackProcessor::Update> readbackUpdates;
+ nsIntRegion readbackRegion;
+ if (aReadback && UsedForReadback()) {
+ aReadback->GetPaintedLayerUpdates(this, &readbackUpdates);
+ }
+
+ IntPoint origin(mVisibleRegion.GetBounds().x, mVisibleRegion.GetBounds().y);
+ mContentClient->BeginPaint();
+ PaintThebes();
+ mContentClient->EndPaint(&readbackUpdates);
+}
+
+already_AddRefed<PaintedLayer>
+ClientLayerManager::CreatePaintedLayer()
+{
+ return CreatePaintedLayerWithHint(NONE);
+}
+
+already_AddRefed<PaintedLayer>
+ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
+{
+ NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
+ if (gfxPrefs::LayersTilesEnabled()) {
+ RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
+ CREATE_SHADOW(Painted);
+ return layer.forget();
+ } else {
+ RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
+ CREATE_SHADOW(Painted);
+ return layer.forget();
+ }
+}
+
+void
+ClientPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mContentClient) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mContentClient->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientPaintedLayer.h b/gfx/layers/client/ClientPaintedLayer.h
new file mode 100644
index 000000000..949f746ae
--- /dev/null
+++ b/gfx/layers/client/ClientPaintedLayer.h
@@ -0,0 +1,127 @@
+/* -*- 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/. */
+
+#ifndef GFX_CLIENTPAINTEDLAYER_H
+#define GFX_CLIENTPAINTEDLAYER_H
+
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "Layers.h" // for PaintedLayer, etc
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/ContentClient.h" // for ContentClient
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "nsRegion.h" // for nsIntRegion
+#include "mozilla/layers/PLayerTransaction.h" // for PaintedLayerAttributes
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ShadowableLayer;
+class SpecificLayerAttributes;
+
+class ClientPaintedLayer : public PaintedLayer,
+ public ClientLayer {
+public:
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ explicit ClientPaintedLayer(ClientLayerManager* aLayerManager,
+ LayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE) :
+ PaintedLayer(aLayerManager, static_cast<ClientLayer*>(this), aCreationHint),
+ mContentClient(nullptr)
+ {
+ MOZ_COUNT_CTOR(ClientPaintedLayer);
+ }
+
+protected:
+ virtual ~ClientPaintedLayer()
+ {
+ if (mContentClient) {
+ mContentClient->OnDetach();
+ mContentClient = nullptr;
+ }
+ MOZ_COUNT_DTOR(ClientPaintedLayer);
+ }
+
+public:
+ virtual void SetVisibleRegion(const LayerIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ PaintedLayer::SetVisibleRegion(aRegion);
+ }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override
+ {
+ NS_ASSERTION(ClientManager()->InConstruction(),
+ "Can only set properties in construction phase");
+ mInvalidRegion.Add(aRegion);
+ mValidRegion.Sub(mValidRegion, mInvalidRegion.GetRegion());
+ }
+
+ virtual void RenderLayer() override { RenderLayerWithReadback(nullptr); }
+
+ virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) override;
+
+ virtual void ClearCachedResources() override
+ {
+ if (mContentClient) {
+ mContentClient->Clear();
+ }
+ mValidRegion.SetEmpty();
+ DestroyBackBuffer();
+ }
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mContentClient) {
+ mContentClient->HandleMemoryPressure();
+ }
+ }
+
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override
+ {
+ aAttrs = PaintedLayerAttributes(GetValidRegion());
+ }
+
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ virtual Layer* AsLayer() override { return this; }
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual CompositableClient* GetCompositableClient() override
+ {
+ return mContentClient;
+ }
+
+ virtual void Disconnect() override
+ {
+ mContentClient = nullptr;
+ ClientLayer::Disconnect();
+ }
+
+protected:
+ void PaintThebes();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+ void DestroyBackBuffer()
+ {
+ mContentClient = nullptr;
+ }
+
+ RefPtr<ContentClient> mContentClient;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/ClientReadbackLayer.h b/gfx/layers/client/ClientReadbackLayer.h
new file mode 100644
index 000000000..5f58486ca
--- /dev/null
+++ b/gfx/layers/client/ClientReadbackLayer.h
@@ -0,0 +1,34 @@
+/* -*- 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/. */
+
+#ifndef GFX_CLIENTREADBACKLAYER_H
+#define GFX_CLIENTREADBACKLAYER_H
+
+#include "ClientLayerManager.h"
+#include "ReadbackLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientReadbackLayer :
+ public ReadbackLayer,
+ public ClientLayer
+{
+public:
+ explicit ClientReadbackLayer(ClientLayerManager *aManager)
+ : ReadbackLayer(aManager, nullptr)
+ {
+ mImplData = static_cast<ClientLayer*>(this);
+ }
+
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+ virtual Layer* AsLayer() override { return this; }
+ virtual void RenderLayer() override {}
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_CLIENTREADBACKLAYER_H */
diff --git a/gfx/layers/client/ClientTiledPaintedLayer.cpp b/gfx/layers/client/ClientTiledPaintedLayer.cpp
new file mode 100644
index 000000000..5a07681f6
--- /dev/null
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -0,0 +1,615 @@
+/* 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 "ClientTiledPaintedLayer.h"
+#include "FrameMetrics.h" // for FrameMetrics
+#include "Units.h" // for ScreenIntRect, CSSPoint, etc
+#include "UnitTransforms.h" // for TransformTo
+#include "ClientLayerManager.h" // for ClientLayerManager, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/Rect.h" // for Rect, RectTyped
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
+#include "mozilla/layers/LayersMessages.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "LayersLogging.h"
+#include "mozilla/layers/SingleTiledContentClient.h"
+
+namespace mozilla {
+namespace layers {
+
+using gfx::Rect;
+using gfx::IntRect;
+using gfx::IntSize;
+
+ClientTiledPaintedLayer::ClientTiledPaintedLayer(ClientLayerManager* const aManager,
+ ClientLayerManager::PaintedLayerCreationHint aCreationHint)
+ : PaintedLayer(aManager, static_cast<ClientLayer*>(this), aCreationHint)
+ , mContentClient()
+ , mHaveSingleTiledContentClient(false)
+{
+ MOZ_COUNT_CTOR(ClientTiledPaintedLayer);
+ mPaintData.mLastScrollOffset = ParentLayerPoint(0, 0);
+ mPaintData.mFirstPaint = true;
+}
+
+ClientTiledPaintedLayer::~ClientTiledPaintedLayer()
+{
+ MOZ_COUNT_DTOR(ClientTiledPaintedLayer);
+}
+
+void
+ClientTiledPaintedLayer::ClearCachedResources()
+{
+ if (mContentClient) {
+ mContentClient->ClearCachedResources();
+ }
+ mValidRegion.SetEmpty();
+ mContentClient = nullptr;
+}
+
+void
+ClientTiledPaintedLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
+{
+ aAttrs = PaintedLayerAttributes(GetValidRegion());
+}
+
+static Maybe<LayerRect>
+ApplyParentLayerToLayerTransform(const ParentLayerToLayerMatrix4x4& aTransform,
+ const ParentLayerRect& aParentLayerRect,
+ const LayerRect& aClip)
+{
+ return UntransformBy(aTransform, aParentLayerRect, aClip);
+}
+
+static LayerToParentLayerMatrix4x4
+GetTransformToAncestorsParentLayer(Layer* aStart, const LayerMetricsWrapper& aAncestor)
+{
+ gfx::Matrix4x4 transform;
+ const LayerMetricsWrapper& ancestorParent = aAncestor.GetParent();
+ for (LayerMetricsWrapper iter(aStart, LayerMetricsWrapper::StartAt::BOTTOM);
+ ancestorParent ? iter != ancestorParent : iter.IsValid();
+ iter = iter.GetParent()) {
+ transform = transform * iter.GetTransform();
+
+ if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+ // When scrolling containers, layout adds a post-scale into the transform
+ // of the displayport-ancestor (which we pick up in GetTransform() above)
+ // to cancel out the pres shell resolution (for historical reasons). The
+ // compositor in turn cancels out this post-scale (i.e., scales by the
+ // pres shell resolution), and to get correct calculations, we need to do
+ // so here, too.
+ //
+ // With containerless scrolling, the offending post-scale is on the
+ // parent layer of the displayport-ancestor, which we don't reach in this
+ // loop, so we don't need to worry about it.
+ float presShellResolution = iter.GetPresShellResolution();
+ transform.PostScale(presShellResolution, presShellResolution, 1.0f);
+ }
+ }
+ return ViewAs<LayerToParentLayerMatrix4x4>(transform);
+}
+
+void
+ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
+ LayerMetricsWrapper* aOutDisplayPortAncestor,
+ bool* aOutHasTransformAnimation)
+{
+ LayerMetricsWrapper scrollAncestor;
+ LayerMetricsWrapper displayPortAncestor;
+ bool hasTransformAnimation = false;
+ for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
+ hasTransformAnimation |= ancestor.HasTransformAnimation();
+ const FrameMetrics& metrics = ancestor.Metrics();
+ if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
+ scrollAncestor = ancestor;
+ }
+ if (!metrics.GetDisplayPort().IsEmpty()) {
+ displayPortAncestor = ancestor;
+ // Any layer that has a displayport must be scrollable, so we can break
+ // here.
+ break;
+ }
+ }
+ if (aOutScrollAncestor) {
+ *aOutScrollAncestor = scrollAncestor;
+ }
+ if (aOutDisplayPortAncestor) {
+ *aOutDisplayPortAncestor = displayPortAncestor;
+ }
+ if (aOutHasTransformAnimation) {
+ *aOutHasTransformAnimation = hasTransformAnimation;
+ }
+}
+
+void
+ClientTiledPaintedLayer::BeginPaint()
+{
+ mPaintData.ResetPaintData();
+
+ if (!GetBaseTransform().Is2D()) {
+ // Give up if there is a complex CSS transform on the layer. We might
+ // eventually support these but for now it's too complicated to handle
+ // given that it's a pretty rare scenario.
+ return;
+ }
+
+ // Get the metrics of the nearest scrollable layer and the nearest layer
+ // with a displayport.
+ LayerMetricsWrapper scrollAncestor;
+ LayerMetricsWrapper displayPortAncestor;
+ bool hasTransformAnimation;
+ GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation);
+
+ if (!displayPortAncestor || !scrollAncestor) {
+ // No displayport or scroll ancestor, so we can't do progressive rendering.
+#if defined(MOZ_WIDGET_ANDROID)
+ // Both Android and b2g on phones are guaranteed to have a displayport set, so this
+ // should never happen.
+ NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor");
+#endif
+ return;
+ }
+
+ TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this,
+ scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation);
+
+ const FrameMetrics& scrollMetrics = scrollAncestor.Metrics();
+ const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics();
+
+ // Calculate the transform required to convert ParentLayer space of our
+ // display port ancestor to the Layer space of this layer.
+ ParentLayerToLayerMatrix4x4 transformDisplayPortToLayer =
+ GetTransformToAncestorsParentLayer(this, displayPortAncestor).Inverse();
+
+ LayerRect layerBounds = ViewAs<LayerPixel>(Rect(GetLayerBounds()));
+
+ // Compute the critical display port that applies to this layer in the
+ // LayoutDevice space of this layer, but only if there is no OMT animation
+ // on this layer. If there is an OMT animation then we need to draw the whole
+ // visible region of this layer as determined by layout, because we don't know
+ // what parts of it might move into view in the compositor.
+ mPaintData.mHasTransformAnimation = hasTransformAnimation;
+ if (!mPaintData.mHasTransformAnimation &&
+ mContentClient->GetLowPrecisionTiledBuffer()) {
+ ParentLayerRect criticalDisplayPort =
+ (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom())
+ + displayportMetrics.GetCompositionBounds().TopLeft();
+ Maybe<LayerRect> criticalDisplayPortTransformed =
+ ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort, layerBounds);
+ if (criticalDisplayPortTransformed) {
+ mPaintData.mCriticalDisplayPort = Some(RoundedToInt(*criticalDisplayPortTransformed));
+ } else {
+ mPaintData.mCriticalDisplayPort = Some(LayerIntRect(0, 0, 0, 0));
+ }
+ }
+ TILING_LOG("TILING %p: Critical displayport %s\n", this,
+ mPaintData.mCriticalDisplayPort ?
+ Stringify(*mPaintData.mCriticalDisplayPort).c_str() : "not set");
+
+ // Store the resolution from the displayport ancestor layer. Because this is Gecko-side,
+ // before any async transforms have occurred, we can use the zoom for this.
+ mPaintData.mResolution = displayportMetrics.GetZoom();
+ TILING_LOG("TILING %p: Resolution %s\n", this, Stringify(mPaintData.mResolution).c_str());
+
+ // Store the applicable composition bounds in this layer's Layer units.
+ mPaintData.mTransformToCompBounds =
+ GetTransformToAncestorsParentLayer(this, scrollAncestor);
+ ParentLayerToLayerMatrix4x4 transformToBounds = mPaintData.mTransformToCompBounds.Inverse();
+ Maybe<LayerRect> compositionBoundsTransformed = ApplyParentLayerToLayerTransform(
+ transformToBounds, scrollMetrics.GetCompositionBounds(), layerBounds);
+ if (compositionBoundsTransformed) {
+ mPaintData.mCompositionBounds = *compositionBoundsTransformed;
+ } else {
+ mPaintData.mCompositionBounds.SetEmpty();
+ }
+ TILING_LOG("TILING %p: Composition bounds %s\n", this, Stringify(mPaintData.mCompositionBounds).c_str());
+
+ // Calculate the scroll offset since the last transaction
+ mPaintData.mScrollOffset = displayportMetrics.GetScrollOffset() * displayportMetrics.GetZoom();
+ TILING_LOG("TILING %p: Scroll offset %s\n", this, Stringify(mPaintData.mScrollOffset).c_str());
+}
+
+bool
+ClientTiledPaintedLayer::IsScrollingOnCompositor(const FrameMetrics& aParentMetrics)
+{
+ CompositorBridgeChild* compositor = nullptr;
+ if (Manager() && Manager()->AsClientLayerManager()) {
+ compositor = Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ FrameMetrics compositorMetrics;
+ if (!compositor->LookupCompositorFrameMetrics(aParentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ // 1 is a tad high for a fuzzy equals epsilon however if our scroll delta
+ // is so small then we have nothing to gain from using paint heuristics.
+ float COORDINATE_EPSILON = 1.f;
+
+ return !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().x,
+ aParentMetrics.GetScrollOffset().x,
+ COORDINATE_EPSILON) ||
+ !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y,
+ aParentMetrics.GetScrollOffset().y,
+ COORDINATE_EPSILON);
+}
+
+bool
+ClientTiledPaintedLayer::UseProgressiveDraw() {
+ if (!gfxPrefs::ProgressivePaint()) {
+ // pref is disabled, so never do progressive
+ return false;
+ }
+
+ if (!mContentClient->GetTiledBuffer()->SupportsProgressiveUpdate()) {
+ return false;
+ }
+
+ if (ClientManager()->HasShadowTarget()) {
+ // This condition is true when we are in a reftest scenario. We don't want
+ // to draw progressively here because it can cause intermittent reftest
+ // failures because the harness won't wait for all the tiles to be drawn.
+ return false;
+ }
+
+ if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) {
+ // This layer is fixed-position and so even if it does have a scrolling
+ // ancestor it will likely be entirely on-screen all the time, so we
+ // should draw it all at once
+ return false;
+ }
+
+ if (mPaintData.mHasTransformAnimation) {
+ // The compositor is going to animate this somehow, so we want it all
+ // on the screen at once.
+ return false;
+ }
+
+ if (ClientManager()->AsyncPanZoomEnabled()) {
+ LayerMetricsWrapper scrollAncestor;
+ GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+ MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is set
+ if (!scrollAncestor) {
+ return false;
+ }
+ const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
+ if (!IsScrollingOnCompositor(parentMetrics)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ // If we have started drawing low-precision already, then we
+ // shouldn't do anything there.
+ if (mPaintData.mLowPrecisionPaintCount != 0) {
+ return false;
+ }
+
+ // Only draw progressively when there is something to paint and the
+ // resolution is unchanged
+ if (!aInvalidRegion.IsEmpty() &&
+ UseProgressiveDraw() &&
+ mContentClient->GetTiledBuffer()->GetFrameResolution() == mPaintData.mResolution) {
+ // Store the old valid region, then clear it before painting.
+ // We clip the old valid region to the visible region, as it only gets
+ // used to decide stale content (currently valid and previously visible)
+ nsIntRegion oldValidRegion = mContentClient->GetTiledBuffer()->GetValidRegion();
+ oldValidRegion.And(oldValidRegion, aVisibleRegion);
+ if (mPaintData.mCriticalDisplayPort) {
+ oldValidRegion.And(oldValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: Progressive update with old valid region %s\n", this, Stringify(oldValidRegion).c_str());
+
+ return mContentClient->GetTiledBuffer()->ProgressiveUpdate(mValidRegion, aInvalidRegion,
+ oldValidRegion, &mPaintData, aCallback, aCallbackData);
+ }
+
+ // Otherwise do a non-progressive paint. We must do this even when
+ // the region to paint is empty as the valid region may have shrunk.
+
+ mValidRegion = aVisibleRegion;
+ if (mPaintData.mCriticalDisplayPort) {
+ mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: Non-progressive paint invalid region %s\n", this, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Non-progressive paint new valid region %s\n", this, Stringify(mValidRegion).c_str());
+
+ mContentClient->GetTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
+ mContentClient->GetTiledBuffer()->PaintThebes(mValidRegion, aInvalidRegion, aInvalidRegion,
+ aCallback, aCallbackData);
+ mPaintData.mPaintFinished = true;
+ return true;
+}
+
+bool
+ClientTiledPaintedLayer::RenderLowPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ // Render the low precision buffer, if the visible region is larger than the
+ // critical display port.
+ if (!mPaintData.mCriticalDisplayPort ||
+ !nsIntRegion(mPaintData.mCriticalDisplayPort->ToUnknownRect()).Contains(aVisibleRegion)) {
+ nsIntRegion oldValidRegion = mContentClient->GetLowPrecisionTiledBuffer()->GetValidRegion();
+ oldValidRegion.And(oldValidRegion, aVisibleRegion);
+
+ bool updatedBuffer = false;
+
+ // If the frame resolution or format have changed, invalidate the buffer
+ if (mContentClient->GetLowPrecisionTiledBuffer()->GetFrameResolution() != mPaintData.mResolution ||
+ mContentClient->GetLowPrecisionTiledBuffer()->HasFormatChanged()) {
+ if (!mLowPrecisionValidRegion.IsEmpty()) {
+ updatedBuffer = true;
+ }
+ oldValidRegion.SetEmpty();
+ mLowPrecisionValidRegion.SetEmpty();
+ mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
+ mContentClient->GetLowPrecisionTiledBuffer()->SetFrameResolution(mPaintData.mResolution);
+ aInvalidRegion = aVisibleRegion;
+ }
+
+ // Invalidate previously valid content that is no longer visible
+ if (mPaintData.mLowPrecisionPaintCount == 1) {
+ mLowPrecisionValidRegion.And(mLowPrecisionValidRegion, aVisibleRegion);
+ }
+ mPaintData.mLowPrecisionPaintCount++;
+
+ // Remove the valid high-precision region from the invalid low-precision
+ // region. We don't want to spend time drawing things twice.
+ aInvalidRegion.Sub(aInvalidRegion, mValidRegion);
+
+ TILING_LOG("TILING %p: Progressive paint: low-precision invalid region is %s\n", this, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive paint: low-precision old valid region is %s\n", this, Stringify(oldValidRegion).c_str());
+
+ if (!aInvalidRegion.IsEmpty()) {
+ updatedBuffer = mContentClient->GetLowPrecisionTiledBuffer()->ProgressiveUpdate(
+ mLowPrecisionValidRegion, aInvalidRegion, oldValidRegion,
+ &mPaintData, aCallback, aCallbackData);
+ }
+
+ TILING_LOG("TILING %p: Progressive paint: low-precision new valid region is %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
+ return updatedBuffer;
+ }
+ if (!mLowPrecisionValidRegion.IsEmpty()) {
+ TILING_LOG("TILING %p: Clearing low-precision buffer\n", this);
+ // Clear the low precision tiled buffer.
+ mLowPrecisionValidRegion.SetEmpty();
+ mContentClient->GetLowPrecisionTiledBuffer()->ResetPaintedAndValidState();
+ // Return true here so we send a Painted callback after clearing the valid
+ // region of the low precision buffer. This allows the shadow buffer's valid
+ // region to be updated and the associated resources to be freed.
+ return true;
+ }
+ return false;
+}
+
+void
+ClientTiledPaintedLayer::EndPaint()
+{
+ mPaintData.mLastScrollOffset = mPaintData.mScrollOffset;
+ mPaintData.mPaintFinished = true;
+ mPaintData.mFirstPaint = false;
+ TILING_LOG("TILING %p: Paint finished\n", this);
+}
+
+void
+ClientTiledPaintedLayer::RenderLayer()
+{
+ LayerManager::DrawPaintedLayerCallback callback =
+ ClientManager()->GetPaintedLayerCallback();
+ void *data = ClientManager()->GetPaintedLayerCallbackData();
+
+ IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size();
+ IntSize tileSize = gfx::gfxVars::TileSize();
+ bool isHalfTileWidthOrHeight = layerSize.width <= tileSize.width / 2 ||
+ layerSize.height <= tileSize.height / 2;
+
+ // Use single tile when layer is not scrollable, is smaller than one
+ // tile, or when more than half of the tiles' pixels in either
+ // dimension would be wasted.
+ bool wantSingleTiledContentClient =
+ (mCreationHint == LayerManager::NONE ||
+ layerSize <= tileSize ||
+ isHalfTileWidthOrHeight) &&
+ SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
+ gfxPrefs::LayersSingleTileEnabled();
+
+ if (mContentClient && mHaveSingleTiledContentClient && !wantSingleTiledContentClient) {
+ mContentClient = nullptr;
+ mValidRegion.SetEmpty();
+ }
+
+ if (!mContentClient) {
+ if (wantSingleTiledContentClient) {
+ mContentClient = new SingleTiledContentClient(*this, ClientManager());
+ mHaveSingleTiledContentClient = true;
+ } else {
+ mContentClient = new MultiTiledContentClient(*this, ClientManager());
+ mHaveSingleTiledContentClient = false;
+ }
+
+ mContentClient->Connect();
+ ClientManager()->AsShadowForwarder()->Attach(mContentClient, this);
+ MOZ_ASSERT(mContentClient->GetForwarder());
+ }
+
+ if (mContentClient->GetTiledBuffer()->HasFormatChanged()) {
+ mValidRegion = nsIntRegion();
+ mContentClient->GetTiledBuffer()->ResetPaintedAndValidState();
+ }
+
+ TILING_LOG("TILING %p: Initial visible region %s\n", this, Stringify(mVisibleRegion).c_str());
+ TILING_LOG("TILING %p: Initial valid region %s\n", this, Stringify(mValidRegion).c_str());
+ TILING_LOG("TILING %p: Initial low-precision valid region %s\n", this, Stringify(mLowPrecisionValidRegion).c_str());
+
+ nsIntRegion neededRegion = mVisibleRegion.ToUnknownRegion();
+#ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
+ // This is handled by PadDrawTargetOutFromRegion in TiledContentClient for mobile
+ if (MayResample()) {
+ // If we're resampling then bilinear filtering can read up to 1 pixel
+ // outside of our texture coords. Make the visible region a single rect,
+ // and pad it out by 1 pixel (restricted to tile boundaries) so that
+ // we always have valid content or transparent pixels to sample from.
+ IntRect bounds = neededRegion.GetBounds();
+ IntRect wholeTiles = bounds;
+ wholeTiles.InflateToMultiple(gfx::gfxVars::TileSize());
+ IntRect padded = bounds;
+ padded.Inflate(1);
+ padded.IntersectRect(padded, wholeTiles);
+ neededRegion = padded;
+ }
+#endif
+
+ nsIntRegion invalidRegion;
+ invalidRegion.Sub(neededRegion, mValidRegion);
+ if (invalidRegion.IsEmpty()) {
+ EndPaint();
+ return;
+ }
+
+ if (!callback) {
+ ClientManager()->SetTransactionIncomplete();
+ return;
+ }
+
+ if (!ClientManager()->IsRepeatTransaction()) {
+ // Only paint the mask layers on the first transaction.
+ RenderMaskLayers(this);
+
+ // For more complex cases we need to calculate a bunch of metrics before we
+ // can do the paint.
+ BeginPaint();
+ if (mPaintData.mPaintFinished) {
+ return;
+ }
+
+ // Make sure that tiles that fall outside of the visible region or outside of the
+ // critical displayport are discarded on the first update. Also make sure that we
+ // only draw stuff inside the critical displayport on the first update.
+ mValidRegion.And(mValidRegion, neededRegion);
+ if (mPaintData.mCriticalDisplayPort) {
+ mValidRegion.And(mValidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+
+ TILING_LOG("TILING %p: First-transaction valid region %s\n", this, Stringify(mValidRegion).c_str());
+ TILING_LOG("TILING %p: First-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
+ } else {
+ if (mPaintData.mCriticalDisplayPort) {
+ invalidRegion.And(invalidRegion, mPaintData.mCriticalDisplayPort->ToUnknownRect());
+ }
+ TILING_LOG("TILING %p: Repeat-transaction invalid region %s\n", this, Stringify(invalidRegion).c_str());
+ }
+
+ nsIntRegion lowPrecisionInvalidRegion;
+ if (mContentClient->GetLowPrecisionTiledBuffer()) {
+ // Calculate the invalid region for the low precision buffer. Make sure
+ // to remove the valid high-precision area so we don't double-paint it.
+ lowPrecisionInvalidRegion.Sub(neededRegion, mLowPrecisionValidRegion);
+ lowPrecisionInvalidRegion.Sub(lowPrecisionInvalidRegion, mValidRegion);
+ }
+ TILING_LOG("TILING %p: Low-precision invalid region %s\n", this, Stringify(lowPrecisionInvalidRegion).c_str());
+
+ bool updatedHighPrecision = RenderHighPrecision(invalidRegion,
+ neededRegion,
+ callback, data);
+ if (updatedHighPrecision) {
+ ClientManager()->Hold(this);
+ mContentClient->UpdatedBuffer(TiledContentClient::TILED_BUFFER);
+
+ if (!mPaintData.mPaintFinished) {
+ // There is still more high-res stuff to paint, so we're not
+ // done yet. A subsequent transaction will take care of this.
+ ClientManager()->SetRepeatTransaction();
+ return;
+ }
+ }
+
+ // If there is nothing to draw in low-precision, then we're done.
+ if (lowPrecisionInvalidRegion.IsEmpty()) {
+ EndPaint();
+ return;
+ }
+
+ if (updatedHighPrecision) {
+ // If there are low precision updates, but we just did some high-precision
+ // updates, then mark the paint as unfinished and request a repeat transaction.
+ // This is so that we don't perform low-precision updates in the same transaction
+ // as high-precision updates.
+ TILING_LOG("TILING %p: Scheduling repeat transaction for low-precision painting\n", this);
+ ClientManager()->SetRepeatTransaction();
+ mPaintData.mLowPrecisionPaintCount = 1;
+ mPaintData.mPaintFinished = false;
+ return;
+ }
+
+ bool updatedLowPrecision = RenderLowPrecision(lowPrecisionInvalidRegion,
+ neededRegion,
+ callback, data);
+ if (updatedLowPrecision) {
+ ClientManager()->Hold(this);
+ mContentClient->UpdatedBuffer(TiledContentClient::LOW_PRECISION_TILED_BUFFER);
+
+ if (!mPaintData.mPaintFinished) {
+ // There is still more low-res stuff to paint, so we're not
+ // done yet. A subsequent transaction will take care of this.
+ ClientManager()->SetRepeatTransaction();
+ return;
+ }
+ }
+
+ // If we get here, we've done all the high- and low-precision
+ // paints we wanted to do, so we can finish the paint and chill.
+ EndPaint();
+}
+
+bool
+ClientTiledPaintedLayer::IsOptimizedFor(LayerManager::PaintedLayerCreationHint aHint)
+{
+ // The only creation hint is whether the layer is scrollable or not, and this
+ // is only respected on B2G and OSX, where it's used to determine whether to
+ // use a tiled content client or not.
+ // There are pretty nasty performance consequences for not using tiles on
+ // large, scrollable layers, so we want the layer to be recreated in this
+ // situation.
+ return aHint == GetCreationHint();
+}
+
+void
+ClientTiledPaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ PaintedLayer::PrintInfo(aStream, aPrefix);
+ if (mContentClient) {
+ aStream << "\n";
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+ mContentClient->PrintInfo(aStream, pfx.get());
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ClientTiledPaintedLayer.h b/gfx/layers/client/ClientTiledPaintedLayer.h
new file mode 100644
index 000000000..9e1e003ee
--- /dev/null
+++ b/gfx/layers/client/ClientTiledPaintedLayer.h
@@ -0,0 +1,153 @@
+/* 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/. */
+
+#ifndef GFX_CLIENTTILEDPAINTEDLAYER_H
+#define GFX_CLIENTTILEDPAINTEDLAYER_H
+
+#include "ClientLayerManager.h" // for ClientLayer, etc
+#include "Layers.h" // for PaintedLayer, etc
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/TiledContentClient.h"
+#include "nsDebug.h" // for NS_RUNTIMEABORT
+#include "nsRegion.h" // for nsIntRegion
+
+namespace mozilla {
+namespace layers {
+
+class ShadowableLayer;
+class SpecificLayerAttributes;
+
+/**
+ * An implementation of PaintedLayer that ONLY supports remote
+ * composition that is backed by tiles. This painted layer implementation
+ * is better suited to mobile hardware to work around slow implementation
+ * of glTexImage2D (for OGL compositors), and restrait memory bandwidth.
+ *
+ * Tiled PaintedLayers use a different protocol compared with other
+ * layers. A copy of the tiled buffer is made and sent to the compositing
+ * thread via the layers protocol. Tiles are uploaded by the buffers
+ * asynchonously without using IPC, that means they are not safe for cross-
+ * process use (bug 747811). Each tile has a TextureHost/Client pair but
+ * they communicate directly rather than using the Texture protocol.
+ *
+ * There is no ContentClient for tiled layers. There is a ContentHost, however.
+ */
+class ClientTiledPaintedLayer : public PaintedLayer,
+ public ClientLayer
+{
+ typedef PaintedLayer Base;
+
+public:
+ explicit ClientTiledPaintedLayer(ClientLayerManager* const aManager,
+ ClientLayerManager::PaintedLayerCreationHint aCreationHint = LayerManager::NONE);
+
+protected:
+ ~ClientTiledPaintedLayer();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+public:
+ // Override name to distinguish it from ClientPaintedLayer in layer dumps
+ virtual const char* Name() const override { return "TiledPaintedLayer"; }
+
+ // PaintedLayer
+ virtual Layer* AsLayer() override { return this; }
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) override {
+ mInvalidRegion.Add(aRegion);
+ nsIntRegion invalidRegion = mInvalidRegion.GetRegion();
+ mValidRegion.Sub(mValidRegion, invalidRegion);
+ mLowPrecisionValidRegion.Sub(mLowPrecisionValidRegion, invalidRegion);
+ }
+
+ // Shadow methods
+ virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) override;
+ virtual ShadowableLayer* AsShadowableLayer() override { return this; }
+
+ virtual void Disconnect() override
+ {
+ ClientLayer::Disconnect();
+ }
+
+ virtual void RenderLayer() override;
+
+ virtual void ClearCachedResources() override;
+
+ virtual void HandleMemoryPressure() override
+ {
+ if (mContentClient) {
+ mContentClient->HandleMemoryPressure();
+ }
+ }
+
+ /**
+ * Helper method to find the nearest ancestor layers which
+ * scroll and have a displayport. The parameters are out-params
+ * which hold the return values; the values passed in may be null.
+ */
+ void GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor,
+ LayerMetricsWrapper* aOutDisplayPortAncestor,
+ bool* aOutHasTransformAnimation);
+
+ virtual bool IsOptimizedFor(LayerManager::PaintedLayerCreationHint aCreationHint) override;
+
+private:
+ ClientLayerManager* ClientManager()
+ {
+ return static_cast<ClientLayerManager*>(mManager);
+ }
+
+ /**
+ * For the initial PaintThebes of a transaction, calculates all the data
+ * needed for that paint and any repeated transactions.
+ */
+ void BeginPaint();
+
+ /**
+ * Check if the layer is being scrolled by APZ on the compositor.
+ */
+ bool IsScrollingOnCompositor(const FrameMetrics& aParentMetrics);
+
+ /**
+ * Check if we should use progressive draw on this layer. We will
+ * disable progressive draw based on a preference or if the layer
+ * is not being scrolled.
+ */
+ bool UseProgressiveDraw();
+
+ /**
+ * Helper function to do the high-precision paint.
+ * This function returns true if it updated the paint buffer.
+ */
+ bool RenderHighPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ /**
+ * Helper function to do the low-precision paint.
+ * This function returns true if it updated the paint buffer.
+ */
+ bool RenderLowPrecision(nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aVisibleRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData);
+
+ /**
+ * This causes the paint to be marked as finished, and updates any data
+ * necessary to persist until the next paint.
+ */
+ void EndPaint();
+
+ RefPtr<TiledContentClient> mContentClient;
+ // Flag to indicate if mContentClient is a SingleTiledContentClient. This is
+ // only valid when mContentClient is non-null.
+ bool mHaveSingleTiledContentClient;
+ nsIntRegion mLowPrecisionValidRegion;
+ BasicTiledLayerPaintData mPaintData;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/CompositableChild.cpp b/gfx/layers/client/CompositableChild.cpp
new file mode 100644
index 000000000..34a0e0696
--- /dev/null
+++ b/gfx/layers/client/CompositableChild.cpp
@@ -0,0 +1,119 @@
+/* -*- 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 "CompositableChild.h"
+#include "CompositableClient.h"
+
+namespace mozilla {
+namespace layers {
+
+/* static */ PCompositableChild*
+CompositableChild::CreateActor()
+{
+ CompositableChild* child = new CompositableChild();
+ child->AddRef();
+ return child;
+}
+
+/* static */ void
+CompositableChild::DestroyActor(PCompositableChild* aChild)
+{
+ static_cast<CompositableChild*>(aChild)->Release();
+}
+
+CompositableChild::CompositableChild()
+ : mCompositableClient(nullptr),
+ mAsyncID(0),
+ mCanSend(true)
+{
+ MOZ_COUNT_CTOR(CompositableChild);
+}
+
+CompositableChild::~CompositableChild()
+{
+ MOZ_COUNT_DTOR(CompositableChild);
+}
+
+bool
+CompositableChild::IsConnected() const
+{
+ return mCompositableClient && mCanSend;
+}
+
+void
+CompositableChild::Init(CompositableClient* aCompositable, uint64_t aAsyncID)
+{
+ mCompositableClient = aCompositable;
+ mAsyncID = aAsyncID;
+}
+
+void
+CompositableChild::RevokeCompositableClient()
+{
+ mCompositableClient = nullptr;
+}
+
+RefPtr<CompositableClient>
+CompositableChild::GetCompositableClient()
+{
+ return mCompositableClient;
+}
+
+void
+CompositableChild::ActorDestroy(ActorDestroyReason)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mCanSend = false;
+
+ if (mCompositableClient) {
+ mCompositableClient->mCompositableChild = nullptr;
+ mCompositableClient = nullptr;
+ }
+}
+
+/* static */ PCompositableChild*
+AsyncCompositableChild::CreateActor()
+{
+ AsyncCompositableChild* child = new AsyncCompositableChild();
+ child->AddRef();
+ return child;
+}
+
+AsyncCompositableChild::AsyncCompositableChild()
+ : mLock("AsyncCompositableChild.mLock")
+{
+}
+
+AsyncCompositableChild::~AsyncCompositableChild()
+{
+}
+
+void
+AsyncCompositableChild::ActorDestroy(ActorDestroyReason)
+{
+ mCanSend = false;
+
+ // We do not revoke CompositableClient::mCompositableChild here, since that
+ // could race with the main thread.
+ RevokeCompositableClient();
+}
+
+void
+AsyncCompositableChild::RevokeCompositableClient()
+{
+ MutexAutoLock lock(mLock);
+ mCompositableClient = nullptr;
+}
+
+RefPtr<CompositableClient>
+AsyncCompositableChild::GetCompositableClient()
+{
+ MutexAutoLock lock(mLock);
+ return CompositableChild::GetCompositableClient();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/CompositableChild.h b/gfx/layers/client/CompositableChild.h
new file mode 100644
index 000000000..381d8051b
--- /dev/null
+++ b/gfx/layers/client/CompositableChild.h
@@ -0,0 +1,91 @@
+/* -*- 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/. */
+
+#ifndef mozilla_gfx_layers_client_CompositableChild_h
+#define mozilla_gfx_layers_client_CompositableChild_h
+
+#include <stdint.h>
+#include "IPDLActor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/layers/PCompositableChild.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class AsyncCompositableChild;
+
+/**
+ * IPDL actor used by CompositableClient to match with its corresponding
+ * CompositableHost on the compositor side.
+ *
+ * CompositableChild is owned by a CompositableClient.
+ */
+class CompositableChild : public PCompositableChild
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableChild)
+
+ static PCompositableChild* CreateActor();
+ static void DestroyActor(PCompositableChild* aChild);
+
+ void Init(CompositableClient* aCompositable, uint64_t aAsyncID);
+ virtual void RevokeCompositableClient();
+
+ virtual void ActorDestroy(ActorDestroyReason) override;
+
+ virtual RefPtr<CompositableClient> GetCompositableClient();
+
+ virtual AsyncCompositableChild* AsAsyncCompositableChild() {
+ return nullptr;
+ }
+
+ uint64_t GetAsyncID() const {
+ return mAsyncID;
+ }
+
+ // These should only be called on the IPDL thread.
+ bool IsConnected() const;
+ bool CanSend() const {
+ return mCanSend;
+ }
+
+protected:
+ CompositableChild();
+ virtual ~CompositableChild();
+
+protected:
+ CompositableClient* mCompositableClient;
+ uint64_t mAsyncID;
+ bool mCanSend;
+};
+
+// This CompositableChild can be used off the main thread.
+class AsyncCompositableChild final : public CompositableChild
+{
+public:
+ static PCompositableChild* CreateActor();
+
+ void RevokeCompositableClient() override;
+ RefPtr<CompositableClient> GetCompositableClient() override;
+
+ void ActorDestroy(ActorDestroyReason) override;
+
+ AsyncCompositableChild* AsAsyncCompositableChild() override {
+ return this;
+ }
+
+protected:
+ AsyncCompositableChild();
+ ~AsyncCompositableChild() override;
+
+private:
+ Mutex mLock;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_gfx_layers_client_CompositableChild_h
diff --git a/gfx/layers/client/CompositableClient.cpp b/gfx/layers/client/CompositableClient.cpp
new file mode 100644
index 000000000..52b9a4637
--- /dev/null
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -0,0 +1,274 @@
+/* -*- 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/CompositableClient.h"
+#include <stdint.h> // for uint64_t, uint32_t
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/layers/CompositableChild.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "mozilla/layers/PCompositableChild.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#ifdef XP_WIN
+#include "gfxWindowsPlatform.h" // for gfxWindowsPlatform
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureD3D9.h"
+#endif
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID)
+{
+ MOZ_ASSERT(aActor);
+
+ mForwarder->AssertInForwarderThread();
+
+ mCompositableChild = static_cast<CompositableChild*>(aActor);
+ mCompositableChild->Init(this, aAsyncID);
+}
+
+/* static */ RefPtr<CompositableClient>
+CompositableClient::FromIPDLActor(PCompositableChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+
+ RefPtr<CompositableClient> client = static_cast<CompositableChild*>(aActor)->GetCompositableClient();
+ if (!client) {
+ return nullptr;
+ }
+
+ client->mForwarder->AssertInForwarderThread();
+ return client;
+}
+
+CompositableClient::CompositableClient(CompositableForwarder* aForwarder,
+ TextureFlags aTextureFlags)
+: mForwarder(aForwarder)
+, mTextureFlags(aTextureFlags)
+{
+ MOZ_COUNT_CTOR(CompositableClient);
+}
+
+CompositableClient::~CompositableClient()
+{
+ MOZ_COUNT_DTOR(CompositableClient);
+ Destroy();
+}
+
+LayersBackend
+CompositableClient::GetCompositorBackendType() const
+{
+ return mForwarder->GetCompositorBackendType();
+}
+
+PCompositableChild*
+CompositableClient::GetIPDLActor() const
+{
+ return mCompositableChild;
+}
+
+bool
+CompositableClient::Connect(ImageContainer* aImageContainer)
+{
+ MOZ_ASSERT(!mCompositableChild);
+ if (!GetForwarder() || GetIPDLActor()) {
+ return false;
+ }
+
+ GetForwarder()->AssertInForwarderThread();
+ GetForwarder()->Connect(this, aImageContainer);
+ return true;
+}
+
+bool
+CompositableClient::IsConnected() const
+{
+ // CanSend() is only reliable in the same thread as the IPDL channel.
+ mForwarder->AssertInForwarderThread();
+ return mCompositableChild && mCompositableChild->IsConnected();
+}
+
+void
+CompositableClient::Destroy()
+{
+ if (!mCompositableChild) {
+ return;
+ }
+
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->Destroy();
+ }
+
+ // Take away our IPDL's actor reference back to us.
+ mCompositableChild->RevokeCompositableClient();
+
+ // Schedule the IPDL actor to be destroyed on the forwarder's thread.
+ mForwarder->Destroy(mCompositableChild);
+ mCompositableChild = nullptr;
+}
+
+uint64_t
+CompositableClient::GetAsyncID() const
+{
+ if (mCompositableChild) {
+ return mCompositableChild->GetAsyncID();
+ }
+ return 0; // zero is always an invalid async ID
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ TextureFlags aTextureFlags)
+{
+ return TextureClient::CreateForRawBufferAccess(GetForwarder(),
+ aFormat, aSize, aMoz2DBackend,
+ aTextureFlags | mTextureFlags);
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateForDrawing(GetForwarder(),
+ aFormat, aSize, aSelector,
+ aTextureFlags | mTextureFlags,
+ aAllocFlags);
+}
+
+already_AddRefed<TextureClient>
+CompositableClient::CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateFromSurface(GetForwarder(),
+ aSurface,
+ aSelector,
+ aTextureFlags | mTextureFlags,
+ aAllocFlags);
+}
+
+bool
+CompositableClient::AddTextureClient(TextureClient* aClient)
+{
+ if(!aClient) {
+ return false;
+ }
+ aClient->SetAddedToCompositableClient();
+ return aClient->InitIPDLActor(mForwarder);
+}
+
+void
+CompositableClient::ClearCachedResources()
+{
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->ShrinkToMinimumSize();
+ }
+}
+
+void
+CompositableClient::HandleMemoryPressure()
+{
+ if (mTextureClientRecycler) {
+ mTextureClientRecycler->ShrinkToMinimumSize();
+ }
+}
+
+void
+CompositableClient::RemoveTexture(TextureClient* aTexture)
+{
+ mForwarder->RemoveTextureFromCompositable(this, aTexture);
+}
+
+TextureClientRecycleAllocator*
+CompositableClient::GetTextureClientRecycler()
+{
+ if (mTextureClientRecycler) {
+ return mTextureClientRecycler;
+ }
+
+ if (!mForwarder) {
+ return nullptr;
+ }
+
+ if(!mForwarder->GetTextureForwarder()->UsesImageBridge()) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ return mTextureClientRecycler;
+ }
+
+ // Handle a case that mForwarder is ImageBridge
+
+ if (InImageBridgeChildThread()) {
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ return mTextureClientRecycler;
+ }
+
+ ReentrantMonitor barrier("CompositableClient::GetTextureClientRecycler");
+ ReentrantMonitorAutoEnter mainThreadAutoMon(barrier);
+ bool done = false;
+
+ RefPtr<Runnable> runnable =
+ NS_NewRunnableFunction([&]() {
+ if (!mTextureClientRecycler) {
+ mTextureClientRecycler = new layers::TextureClientRecycleAllocator(mForwarder);
+ }
+ ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
+ done = true;
+ barrier.NotifyAll();
+ });
+
+ ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
+
+ // should stop the thread until done.
+ while (!done) {
+ barrier.Wait();
+ }
+
+ return mTextureClientRecycler;
+}
+
+void
+CompositableClient::DumpTextureClient(std::stringstream& aStream,
+ TextureClient* aTexture,
+ TextureDumpMode aCompress)
+{
+ if (!aTexture) {
+ return;
+ }
+ RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
+ if (!dSurf) {
+ return;
+ }
+ if (aCompress == TextureDumpMode::Compress) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ } else {
+ aStream << gfxUtils::GetAsDataURI(dSurf).get();
+ }
+}
+
+AutoRemoveTexture::~AutoRemoveTexture()
+{
+ if (mCompositable && mTexture && mCompositable->IsConnected()) {
+ mCompositable->RemoveTexture(mTexture);
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h
new file mode 100644
index 000000000..07df59d59
--- /dev/null
+++ b/gfx/layers/client/CompositableClient.h
@@ -0,0 +1,209 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_BUFFERCLIENT_H
+#define MOZILLA_GFX_BUFFERCLIENT_H
+
+#include <stdint.h> // for uint64_t
+#include <vector> // for vector
+#include <map> // for map
+#include "mozilla/Assertions.h" // for MOZ_CRASH
+#include "mozilla/RefPtr.h" // for already_AddRefed, RefCounted
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayersTypes.h" // for LayersBackend, TextureDumpMode
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+
+namespace mozilla {
+namespace layers {
+
+class CompositableClient;
+class ImageBridgeChild;
+class ImageContainer;
+class CompositableForwarder;
+class CompositableChild;
+class PCompositableChild;
+class TextureClientRecycleAllocator;
+
+/**
+ * CompositableClient manages the texture-specific logic for composite layers,
+ * independently of the layer. It is the content side of a CompositableClient/
+ * CompositableHost pair.
+ *
+ * CompositableClient's purpose is to send texture data to the compositor side
+ * along with any extra information about how the texture is to be composited.
+ * Things like opacity or transformation belong to layer and not compositable.
+ *
+ * Since Compositables are independent of layers it is possible to create one,
+ * connect it to the compositor side, and start sending images to it. This alone
+ * is arguably not very useful, but it means that as long as a shadow layer can
+ * do the proper magic to find a reference to the right CompositableHost on the
+ * Compositor side, a Compositable client can be used outside of the main
+ * shadow layer forwarder machinery that is used on the main thread.
+ *
+ * The first step is to create a Compositable client and call Connect().
+ * Connect() creates the underlying IPDL actor (see CompositableChild) and the
+ * corresponding CompositableHost on the other side.
+ *
+ * To do in-transaction texture transfer (the default), call
+ * ShadowLayerForwarder::Attach(CompositableClient*, ShadowableLayer*). This
+ * will let the LayerComposite on the compositor side know which CompositableHost
+ * to use for compositing.
+ *
+ * To do async texture transfer (like async-video), the CompositableClient
+ * should be created with a different CompositableForwarder (like
+ * ImageBridgeChild) and attachment is done with
+ * CompositableForwarder::AttachAsyncCompositable that takes an identifier
+ * instead of a CompositableChild, since the CompositableClient is not managed
+ * by this layer forwarder (the matching uses a global map on the compositor side,
+ * see CompositableMap in ImageBridgeParent.cpp)
+ *
+ * Subclasses: Painted layers use ContentClients, ImageLayers use ImageClients,
+ * Canvas layers use CanvasClients (but ImageHosts). We have a different subclass
+ * where we have a different way of interfacing with the textures - in terms of
+ * drawing into the compositable and/or passing its contents to the compostior.
+ */
+class CompositableClient
+{
+protected:
+ virtual ~CompositableClient();
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositableClient)
+
+ explicit CompositableClient(CompositableForwarder* aForwarder, TextureFlags aFlags = TextureFlags::NO_FLAGS);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) {};
+
+ virtual TextureInfo GetTextureInfo() const = 0;
+
+ LayersBackend GetCompositorBackendType() const;
+
+ already_AddRefed<TextureClient>
+ CreateBufferTextureClient(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend = gfx::BackendType::NONE,
+ TextureFlags aFlags = TextureFlags::DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateTextureClientForDrawing(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateTextureClientFromSurface(gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ /**
+ * Establishes the connection with compositor side through IPDL
+ */
+ virtual bool Connect(ImageContainer* aImageContainer = nullptr);
+
+ void Destroy();
+
+ bool IsConnected() const;
+
+ PCompositableChild* GetIPDLActor() const;
+
+ CompositableForwarder* GetForwarder() const
+ {
+ return mForwarder;
+ }
+
+ /**
+ * This identifier is what lets us attach async compositables with a shadow
+ * layer. It is not used if the compositable is used with the regular shadow
+ * layer forwarder.
+ *
+ * If this returns zero, it means the compositable is not async (it is used
+ * on the main thread).
+ */
+ uint64_t GetAsyncID() const;
+
+ /**
+ * Tells the Compositor to create a TextureHost for this TextureClient.
+ */
+ virtual bool AddTextureClient(TextureClient* aClient);
+
+ /**
+ * A hook for the when the Compositable is detached from it's layer.
+ */
+ virtual void OnDetach() {}
+
+ /**
+ * Clear any resources that are not immediately necessary. This may be called
+ * in low-memory conditions.
+ */
+ virtual void ClearCachedResources();
+
+ /**
+ * Shrink memory usage.
+ * Called when "memory-pressure" is observed.
+ */
+ virtual void HandleMemoryPressure();
+
+ /**
+ * Should be called when deataching a TextureClient from a Compositable, because
+ * some platforms need to do some extra book keeping when this happens (for
+ * example to properly keep track of fences on Gonk).
+ *
+ * See AutoRemoveTexture to automatically invoke this at the end of a scope.
+ */
+ virtual void RemoveTexture(TextureClient* aTexture);
+
+ static RefPtr<CompositableClient> FromIPDLActor(PCompositableChild* aActor);
+
+ void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0);
+
+ TextureFlags GetTextureFlags() const { return mTextureFlags; }
+
+ TextureClientRecycleAllocator* GetTextureClientRecycler();
+
+ bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
+
+ static void DumpTextureClient(std::stringstream& aStream,
+ TextureClient* aTexture,
+ TextureDumpMode aCompress);
+protected:
+ RefPtr<CompositableChild> mCompositableChild;
+ RefPtr<CompositableForwarder> mForwarder;
+ // Some layers may want to enforce some flags to all their textures
+ // (like disallowing tiling)
+ TextureFlags mTextureFlags;
+ RefPtr<TextureClientRecycleAllocator> mTextureClientRecycler;
+
+ friend class CompositableChild;
+};
+
+/**
+ * Helper to call RemoveTexture at the end of a scope.
+ */
+struct AutoRemoveTexture
+{
+ explicit AutoRemoveTexture(CompositableClient* aCompositable,
+ TextureClient* aTexture = nullptr)
+ : mTexture(aTexture)
+ , mCompositable(aCompositable)
+ {}
+
+ ~AutoRemoveTexture();
+
+ RefPtr<TextureClient> mTexture;
+private:
+ CompositableClient* mCompositable;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
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
diff --git a/gfx/layers/client/ContentClient.h b/gfx/layers/client/ContentClient.h
new file mode 100644
index 000000000..d26f01464
--- /dev/null
+++ b/gfx/layers/client/ContentClient.h
@@ -0,0 +1,412 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_CONTENTCLIENT_H
+#define MOZILLA_GFX_CONTENTCLIENT_H
+
+#include <stdint.h> // for uint32_t
+#include "RotatedBuffer.h" // for RotatedContentBuffer, etc
+#include "gfxTypes.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_CRASH
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/TextureClient.h" // for TextureClient
+#include "mozilla/mozalloc.h" // for operator delete
+#include "ReadbackProcessor.h" // For ReadbackProcessor::Update
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray
+
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+} // namespace gfx
+
+namespace layers {
+
+class PaintedLayer;
+
+/**
+ * A compositable client for PaintedLayers. These are different to Image/Canvas
+ * clients due to sending a valid region across IPC and because we do a lot more
+ * optimisation work, encapsualted in RotatedContentBuffers.
+ *
+ * We use content clients for OMTC and non-OMTC, basic rendering so that
+ * BasicPaintedLayer has only one interface to deal with. We support single and
+ * double buffered flavours. For tiled layers, we do not use a ContentClient
+ * although we do have a ContentHost, and we do use texture clients and texture
+ * hosts.
+ *
+ * The interface presented by ContentClient is used by the BasicPaintedLayer
+ * methods - PaintThebes, which is the same for MT and OMTC, and PaintBuffer
+ * which is different (the OMTC one does a little more). The 'buffer' in the
+ * names of a lot of these method is actually the TextureClient. But, 'buffer'
+ * for the RotatedContentBuffer (as in SetBuffer) means a gfxSurface. See the
+ * comments for SetBuffer and SetBufferProvider in RotatedContentBuffer. To keep
+ * these mapped buffers alive, we store a pointer in mOldTextures if the
+ * RotatedContentBuffer's surface is not the one from our texture client, once we
+ * are done painting we unmap the surface/texture client and don't need to keep
+ * it alive anymore, so we clear mOldTextures.
+ *
+ * The sequence for painting is: call BeginPaint on the content client;
+ * call BeginPaintBuffer on the content client. That will initialise the buffer
+ * for painting, by calling RotatedContentBuffer::BeginPaint (usually) which
+ * will call back to ContentClient::FinalizeFrame to finalize update of the
+ * buffers before drawing (i.e., it finalizes the previous frame). Then call
+ * BorrowDrawTargetForPainting to get a DrawTarget to paint into. Then paint.
+ * Then return that DrawTarget using ReturnDrawTarget.
+ * Call EndPaint on the content client;
+ *
+ * SwapBuffers is called in response to the transaction reply from the compositor.
+ */
+class ContentClient : public CompositableClient
+{
+public:
+ /**
+ * Creates, configures, and returns a new content client. If necessary, a
+ * message will be sent to the compositor to create a corresponding content
+ * host.
+ */
+ static already_AddRefed<ContentClient> CreateContentClient(CompositableForwarder* aFwd);
+
+ explicit ContentClient(CompositableForwarder* aForwarder)
+ : CompositableClient(aForwarder)
+ {}
+ virtual ~ContentClient()
+ {}
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ virtual void Clear() = 0;
+ virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) = 0;
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
+
+ // Called as part of the layers transation reply. Conveys data about our
+ // buffer(s) from the compositor. If appropriate we should swap references
+ // to our buffers.
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
+
+ // call before and after painting into this content client
+ virtual void BeginPaint() {}
+ virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
+};
+
+/**
+ * A ContentClient for use with OMTC.
+ */
+class ContentClientRemote : public ContentClient
+{
+public:
+ explicit ContentClientRemote(CompositableForwarder* aForwarder)
+ : ContentClient(aForwarder)
+ {}
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) = 0;
+};
+
+// thin wrapper around RotatedContentBuffer, for on-mtc
+class ContentClientBasic final : public ContentClient
+ , protected RotatedContentBuffer
+{
+public:
+ explicit ContentClientBasic(gfx::BackendType aBackend);
+
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ virtual void Clear() override { RotatedContentBuffer::Clear(); }
+ virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) override
+ {
+ return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
+ }
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+ {
+ return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
+ }
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
+ {
+ BorrowDrawTarget::ReturnDrawTarget(aReturned);
+ }
+
+ void DrawTo(PaintedLayer* aLayer,
+ gfx::DrawTarget* aTarget,
+ float aOpacity,
+ gfx::CompositionOp aOp,
+ gfx::SourceSurface* aMask,
+ const gfx::Matrix* aMaskTransform)
+ {
+ RotatedContentBuffer::DrawTo(aLayer, aTarget, aOpacity, aOp,
+ aMask, aMaskTransform);
+ }
+
+ virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ MOZ_CRASH("GFX: Should not be called on non-remote ContentClient");
+ }
+
+private:
+ gfx::BackendType mBackend;
+};
+
+/**
+ * A ContentClientRemote backed by a RotatedContentBuffer.
+ *
+ * When using a ContentClientRemote, SurfaceDescriptors are created on
+ * the rendering side and destroyed on the compositing side. They are only
+ * passed from one side to the other when the TextureClient/Hosts are created.
+ * *Ownership* of the SurfaceDescriptor moves from the rendering side to the
+ * compositing side with the create message (send from CreateBuffer) which
+ * tells the compositor that TextureClients have been created and that the
+ * compositor should assign the corresponding TextureHosts to our corresponding
+ * ContentHost.
+ *
+ * If the size or type of our buffer(s) change(s), then we simply destroy and
+ * create them.
+ */
+// Version using new texture clients
+class ContentClientRemoteBuffer : public ContentClientRemote
+ , protected RotatedContentBuffer
+{
+ using RotatedContentBuffer::BufferRect;
+ using RotatedContentBuffer::BufferRotation;
+public:
+ explicit ContentClientRemoteBuffer(CompositableForwarder* aForwarder)
+ : ContentClientRemote(aForwarder)
+ , RotatedContentBuffer(ContainsVisibleBounds)
+ , mIsNewBuffer(false)
+ , mFrontAndBackBufferDiffer(false)
+ , mSurfaceFormat(gfx::SurfaceFormat::B8G8R8A8)
+ {}
+
+ typedef RotatedContentBuffer::PaintState PaintState;
+ typedef RotatedContentBuffer::ContentType ContentType;
+
+ virtual void Clear() override
+ {
+ RotatedContentBuffer::Clear();
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ }
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
+ virtual PaintState BeginPaintBuffer(PaintedLayer* aLayer,
+ uint32_t aFlags) override
+ {
+ return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
+ }
+ virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
+ RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+ {
+ return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
+ }
+ virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
+ {
+ BorrowDrawTarget::ReturnDrawTarget(aReturned);
+ }
+
+ /**
+ * Begin/End Paint map a gfxASurface from the texture client
+ * into the buffer of RotatedBuffer. The surface is only
+ * valid when the texture client is locked, so is mapped out
+ * of RotatedContentBuffer when we are done painting.
+ * None of the underlying buffer attributes (rect, rotation)
+ * are affected by mapping/unmapping.
+ */
+ virtual void BeginPaint() override;
+ virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) override;
+
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
+
+ // Expose these protected methods from the superclass.
+ virtual const gfx::IntRect& BufferRect() const
+ {
+ return RotatedContentBuffer::BufferRect();
+ }
+ virtual const nsIntPoint& BufferRotation() const
+ {
+ return RotatedContentBuffer::BufferRotation();
+ }
+
+ virtual void CreateBuffer(ContentType aType, const gfx::IntRect& aRect, uint32_t aFlags,
+ RefPtr<gfx::DrawTarget>* aBlackDT, RefPtr<gfx::DrawTarget>* aWhiteDT) override;
+
+ virtual TextureFlags ExtraTextureFlags() const
+ {
+ return TextureFlags::NO_FLAGS;
+ }
+
+protected:
+ void DestroyBuffers();
+
+ virtual nsIntRegion GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy);
+
+ void BuildTextureClients(gfx::SurfaceFormat aFormat,
+ const gfx::IntRect& aRect,
+ uint32_t aFlags);
+
+ void CreateBackBuffer(const gfx::IntRect& aBufferRect);
+
+ // Ensure we have a valid back buffer if we have a valid front buffer (i.e.
+ // if a backbuffer has been created.)
+ virtual void EnsureBackBufferIfFrontBuffer() {}
+
+ // Create the front buffer for the ContentClient/Host pair if necessary
+ // and notify the compositor that we have created the buffer(s).
+ virtual void DestroyFrontBuffer() {}
+
+ virtual void AbortTextureClientCreation()
+ {
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ mIsNewBuffer = false;
+ }
+
+ RefPtr<TextureClient> mTextureClient;
+ RefPtr<TextureClient> mTextureClientOnWhite;
+ // keep a record of texture clients we have created and need to keep around
+ // (for RotatedBuffer to access), then unlock and remove them when we are done
+ // painting.
+ nsTArray<RefPtr<TextureClient> > mOldTextures;
+
+ bool mIsNewBuffer;
+ bool mFrontAndBackBufferDiffer;
+ gfx::IntSize mSize;
+ gfx::SurfaceFormat mSurfaceFormat;
+};
+
+/**
+ * A double buffered ContentClient. mTextureClient is the back buffer, which
+ * we draw into. mFrontClient is the front buffer which we may read from, but
+ * not write to, when the compositor does not have the 'soft' lock. We can write
+ * into mTextureClient at any time.
+ *
+ * The ContentHost keeps a reference to both corresponding texture hosts, in
+ * response to our UpdateTextureRegion message, the compositor swaps its
+ * references. In response to the compositor's reply we swap our references
+ * (in SwapBuffers).
+ */
+class ContentClientDoubleBuffered : public ContentClientRemoteBuffer
+{
+public:
+ explicit ContentClientDoubleBuffered(CompositableForwarder* aFwd)
+ : ContentClientRemoteBuffer(aFwd)
+ {}
+
+ virtual ~ContentClientDoubleBuffered() {}
+
+ virtual void Clear() override
+ {
+ ContentClientRemoteBuffer::Clear();
+ mFrontClient = nullptr;
+ mFrontClientOnWhite = nullptr;
+ }
+
+ virtual void Updated(const nsIntRegion& aRegionToDraw,
+ const nsIntRegion& aVisibleRegion,
+ bool aDidSelfCopy) override;
+
+ virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
+
+ virtual void BeginPaint() override;
+
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
+
+ virtual void EnsureBackBufferIfFrontBuffer() override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
+ }
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+protected:
+ virtual void DestroyFrontBuffer() override;
+
+private:
+ void UpdateDestinationFrom(const RotatedBuffer& aSource,
+ const nsIntRegion& aUpdateRegion);
+
+ virtual void AbortTextureClientCreation() override
+ {
+ mTextureClient = nullptr;
+ mTextureClientOnWhite = nullptr;
+ mFrontClient = nullptr;
+ mFrontClientOnWhite = nullptr;
+ }
+
+ RefPtr<TextureClient> mFrontClient;
+ RefPtr<TextureClient> mFrontClientOnWhite;
+ nsIntRegion mFrontUpdatedRegion;
+ gfx::IntRect mFrontBufferRect;
+ nsIntPoint mFrontBufferRotation;
+};
+
+/**
+ * A single buffered ContentClient. We have a single TextureClient/Host
+ * which we update and then send a message to the compositor that we are
+ * done updating. It is not safe for the compositor to use the corresponding
+ * TextureHost's memory directly, it must upload it to video memory of some
+ * kind. We are free to modify the TextureClient once we receive reply from
+ * the compositor.
+ */
+class ContentClientSingleBuffered : public ContentClientRemoteBuffer
+{
+public:
+ explicit ContentClientSingleBuffered(CompositableForwarder* aFwd)
+ : ContentClientRemoteBuffer(aFwd)
+ {
+ }
+ virtual ~ContentClientSingleBuffered() {}
+
+ virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_SINGLE, mTextureFlags | ExtraTextureFlags());
+ }
+
+ virtual TextureFlags ExtraTextureFlags() const override
+ {
+ return TextureFlags::IMMEDIATE_UPLOAD;
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/GPUVideoTextureClient.cpp b/gfx/layers/client/GPUVideoTextureClient.cpp
new file mode 100644
index 000000000..10d2bbf38
--- /dev/null
+++ b/gfx/layers/client/GPUVideoTextureClient.cpp
@@ -0,0 +1,72 @@
+/* -*- 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 "GPUVideoTextureClient.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+#include "mozilla/gfx/2D.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+GPUVideoTextureData::GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize)
+ : mManager(aManager)
+ , mSD(aSD)
+ , mSize(aSize)
+{}
+
+GPUVideoTextureData::~GPUVideoTextureData()
+{
+}
+
+bool
+GPUVideoTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = mSD;
+ return true;
+}
+
+void
+GPUVideoTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSize;
+ // TODO: We should probably try do better for this.
+ // layers::Image doesn't expose a format, so it's hard
+ // to figure out in VideoDecoderParent.
+ aInfo.format = SurfaceFormat::B8G8R8X8;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+already_AddRefed<SourceSurface>
+GPUVideoTextureData::GetAsSourceSurface()
+{
+ return mManager->Readback(mSD);
+}
+
+void
+GPUVideoTextureData::Deallocate(LayersIPCChannel* aAllocator)
+{
+ mManager->DeallocateSurfaceDescriptorGPUVideo(mSD);
+ mSD = SurfaceDescriptorGPUVideo();
+}
+
+void
+GPUVideoTextureData::Forget(LayersIPCChannel* aAllocator)
+{
+ // We always need to manually deallocate on the client side.
+ // Ideally we'd set up our TextureClient with the DEALLOCATE_CLIENT
+ // flag, but that forces texture destruction to be synchronous.
+ // Instead let's just deallocate from here as well.
+ Deallocate(aAllocator);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/GPUVideoTextureClient.h b/gfx/layers/client/GPUVideoTextureClient.h
new file mode 100644
index 000000000..a445e2a7d
--- /dev/null
+++ b/gfx/layers/client/GPUVideoTextureClient.h
@@ -0,0 +1,56 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
+#define MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
+
+#include "mozilla/layers/TextureClient.h"
+
+namespace mozilla {
+namespace gfx {
+class SourceSurface;
+}
+namespace dom {
+class VideoDecoderManagerChild;
+}
+namespace layers {
+
+class GPUVideoTextureData : public TextureData
+{
+public:
+ GPUVideoTextureData(dom::VideoDecoderManagerChild* aManager,
+ const SurfaceDescriptorGPUVideo& aSD,
+ const gfx::IntSize& aSize);
+ ~GPUVideoTextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override { return true; };
+
+ virtual void Unlock() override {};
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) override;
+
+ virtual void Forget(LayersIPCChannel* aAllocator) override;
+
+ already_AddRefed<gfx::SourceSurface> GetAsSourceSurface();
+
+ virtual GPUVideoTextureData* AsGPUVideoTextureData() override
+ {
+ return this;
+ }
+
+protected:
+ RefPtr<dom::VideoDecoderManagerChild> mManager;
+ SurfaceDescriptorGPUVideo mSD;
+ gfx::IntSize mSize;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_GPUVIDEOTEXTURECLIENT_H
diff --git a/gfx/layers/client/ImageClient.cpp b/gfx/layers/client/ImageClient.cpp
new file mode 100644
index 000000000..1ccb69502
--- /dev/null
+++ b/gfx/layers/client/ImageClient.cpp
@@ -0,0 +1,300 @@
+/* -*- 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 "ImageClient.h"
+
+#include <stdint.h> // for uint32_t
+
+#include "ClientLayerManager.h" // for ClientLayer
+#include "ImageContainer.h" // for Image, PlanarYCbCrImage, etc
+#include "ImageTypes.h" // for ImageFormat::PLANAR_YCBCR, etc
+#include "GLImages.h" // for SurfaceTextureImage::Data, etc
+#include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BaseSize.h" // for BaseSize
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat, etc
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/layers/TextureClientOGL.h" // for SurfaceTextureClient
+#include "mozilla/mozalloc.h" // for operator delete, etc
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
+#include "nsISupportsImpl.h" // for Image::Release, etc
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+/* static */ already_AddRefed<ImageClient>
+ImageClient::CreateImageClient(CompositableType aCompositableHostType,
+ CompositableForwarder* aForwarder,
+ TextureFlags aFlags)
+{
+ RefPtr<ImageClient> result = nullptr;
+ switch (aCompositableHostType) {
+ case CompositableType::IMAGE:
+ result = new ImageClientSingle(aForwarder, aFlags, CompositableType::IMAGE);
+ break;
+ case CompositableType::IMAGE_BRIDGE:
+ result = new ImageClientBridge(aForwarder, aFlags);
+ break;
+ case CompositableType::UNKNOWN:
+ result = nullptr;
+ break;
+ default:
+ MOZ_CRASH("GFX: unhandled program type image");
+ }
+
+ NS_ASSERTION(result, "Failed to create ImageClient");
+
+ return result.forget();
+}
+
+void
+ImageClient::RemoveTexture(TextureClient* aTexture)
+{
+ GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
+}
+
+ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
+ TextureFlags aFlags,
+ CompositableType aType)
+ : ImageClient(aFwd, aFlags, aType)
+{
+}
+
+TextureInfo ImageClientSingle::GetTextureInfo() const
+{
+ return TextureInfo(CompositableType::IMAGE);
+}
+
+void
+ImageClientSingle::FlushAllImages()
+{
+ MOZ_ASSERT(GetForwarder()->GetTextureForwarder()->UsesImageBridge());
+
+ for (auto& b : mBuffers) {
+ RemoveTexture(b.mTextureClient);
+ }
+ mBuffers.Clear();
+}
+
+/* static */ already_AddRefed<TextureClient>
+ImageClient::CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder)
+{
+ RefPtr<TextureClient> texture;
+ if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
+ PlanarYCbCrImage* ycbcr = static_cast<PlanarYCbCrImage*>(aImage);
+ const PlanarYCbCrData* data = ycbcr->GetData();
+ if (!data) {
+ return nullptr;
+ }
+ texture = TextureClient::CreateForYCbCr(aForwarder,
+ data->mYSize, data->mCbCrSize, data->mStereoMode,
+ data->mYUVColorSpace,
+ TextureFlags::DEFAULT);
+ if (!texture) {
+ return nullptr;
+ }
+
+ TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ bool status = UpdateYCbCrTextureClient(texture, *data);
+ MOZ_ASSERT(status);
+ if (!status) {
+ return nullptr;
+ }
+ } else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
+ aImage->GetFormat() == ImageFormat::EGLIMAGE) {
+ gfx::IntSize size = aImage->GetSize();
+
+ if (aImage->GetFormat() == ImageFormat::EGLIMAGE) {
+ EGLImageImage* typedImage = aImage->AsEGLImageImage();
+ texture = EGLImageTextureData::CreateTextureClient(
+ typedImage, size, aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
+#ifdef MOZ_WIDGET_ANDROID
+ } else if (aImage->GetFormat() == ImageFormat::SURFACE_TEXTURE) {
+ SurfaceTextureImage* typedImage = aImage->AsSurfaceTextureImage();
+ texture = AndroidSurfaceTextureData::CreateTextureClient(
+ typedImage->GetSurfaceTexture(), size, typedImage->GetOriginPos(),
+ aForwarder->GetTextureForwarder(), TextureFlags::DEFAULT);
+#endif
+ } else {
+ MOZ_ASSERT(false, "Bad ImageFormat.");
+ }
+ } else {
+ RefPtr<gfx::SourceSurface> surface = aImage->GetAsSourceSurface();
+ MOZ_ASSERT(surface);
+ texture = TextureClient::CreateForDrawing(aForwarder, surface->GetFormat(), aImage->GetSize(),
+ BackendSelector::Content, TextureFlags::DEFAULT);
+ if (!texture) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(texture->CanExposeDrawTarget());
+
+ if (!texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
+ return nullptr;
+ }
+
+ {
+ // We must not keep a reference to the DrawTarget after it has been unlocked.
+ DrawTarget* dt = texture->BorrowDrawTarget();
+ if (!dt) {
+ gfxWarning() << "ImageClientSingle::UpdateImage failed in BorrowDrawTarget";
+ return nullptr;
+ }
+ MOZ_ASSERT(surface.get());
+ dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
+ }
+
+ texture->Unlock();
+ }
+ return texture.forget();
+}
+
+bool
+ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+{
+ AutoTArray<ImageContainer::OwningImage,4> images;
+ uint32_t generationCounter;
+ aContainer->GetCurrentImages(&images, &generationCounter);
+
+ if (mLastUpdateGenerationCounter == generationCounter) {
+ return true;
+ }
+ mLastUpdateGenerationCounter = generationCounter;
+
+ for (int32_t i = images.Length() - 1; i >= 0; --i) {
+ if (!images[i].mImage->IsValid()) {
+ // Don't try to update to an invalid image.
+ images.RemoveElementAt(i);
+ }
+ }
+ if (images.IsEmpty()) {
+ // This can happen if a ClearAllImages raced with SetCurrentImages from
+ // another thread and ClearImagesFromImageBridge ran after the
+ // SetCurrentImages call but before UpdateImageClientNow.
+ // This can also happen if all images in the list are invalid.
+ // We return true because the caller would attempt to recreate the
+ // ImageClient otherwise, and that isn't going to help.
+ return true;
+ }
+
+ nsTArray<Buffer> newBuffers;
+ AutoTArray<CompositableForwarder::TimedTextureClient,4> textures;
+
+ for (auto& img : images) {
+ Image* image = img.mImage;
+
+ RefPtr<TextureClient> texture = image->GetTextureClient(GetForwarder());
+ const bool hasTextureClient = !!texture;
+
+ for (int32_t i = mBuffers.Length() - 1; i >= 0; --i) {
+ if (mBuffers[i].mImageSerial == image->GetSerial()) {
+ if (hasTextureClient) {
+ MOZ_ASSERT(image->GetTextureClient(GetForwarder()) == mBuffers[i].mTextureClient);
+ } else {
+ texture = mBuffers[i].mTextureClient;
+ }
+ // Remove this element from mBuffers so mBuffers only contains
+ // images that aren't present in 'images'
+ mBuffers.RemoveElementAt(i);
+ }
+ }
+
+ if (!texture) {
+ // Slow path, we should not be hitting it very often and if we do it means
+ // we are using an Image class that is not backed by textureClient and we
+ // should fix it.
+ texture = CreateTextureClientForImage(image, GetForwarder());
+ }
+ if (!texture || !AddTextureClient(texture)) {
+ return false;
+ }
+
+
+ CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
+ t->mTextureClient = texture;
+ t->mTimeStamp = img.mTimeStamp;
+ t->mPictureRect = image->GetPictureRect();
+ t->mFrameID = img.mFrameID;
+ t->mProducerID = img.mProducerID;
+
+ Buffer* newBuf = newBuffers.AppendElement();
+ newBuf->mImageSerial = image->GetSerial();
+ newBuf->mTextureClient = texture;
+
+ texture->SyncWithObject(GetForwarder()->GetSyncObject());
+ }
+
+ GetForwarder()->UseTextures(this, textures);
+
+ for (auto& b : mBuffers) {
+ RemoveTexture(b.mTextureClient);
+ }
+ mBuffers.SwapElements(newBuffers);
+
+ return true;
+}
+
+bool
+ImageClientSingle::AddTextureClient(TextureClient* aTexture)
+{
+ MOZ_ASSERT((mTextureFlags & aTexture->GetFlags()) == mTextureFlags);
+ return CompositableClient::AddTextureClient(aTexture);
+}
+
+void
+ImageClientSingle::OnDetach()
+{
+ mBuffers.Clear();
+}
+
+ImageClient::ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
+ CompositableType aType)
+: CompositableClient(aFwd, aFlags)
+, mLayer(nullptr)
+, mType(aType)
+, mLastUpdateGenerationCounter(0)
+{}
+
+ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
+ TextureFlags aFlags)
+: ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE)
+, mAsyncContainerID(0)
+{
+}
+
+bool
+ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+{
+ if (!GetForwarder() || !mLayer) {
+ return false;
+ }
+ if (mAsyncContainerID == aContainer->GetAsyncContainerID()) {
+ return true;
+ }
+ mAsyncContainerID = aContainer->GetAsyncContainerID();
+ static_cast<ShadowLayerForwarder*>(GetForwarder())->AttachAsyncCompositable(mAsyncContainerID, mLayer);
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/ImageClient.h b/gfx/layers/client/ImageClient.h
new file mode 100644
index 000000000..4c6e26400
--- /dev/null
+++ b/gfx/layers/client/ImageClient.h
@@ -0,0 +1,138 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_IMAGECLIENT_H
+#define MOZILLA_GFX_IMAGECLIENT_H
+
+#include <stdint.h> // for uint32_t, uint64_t
+#include <sys/types.h> // for int32_t
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr, already_AddRefed
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for CompositableType, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/layers/TextureClient.h" // for TextureClient, etc
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsRect.h" // for mozilla::gfx::IntRect
+
+namespace mozilla {
+namespace layers {
+
+class ClientLayer;
+class CompositableForwarder;
+class Image;
+class ImageContainer;
+class ShadowableLayer;
+class ImageClientSingle;
+
+/**
+ * Image clients are used by basic image layers on the content thread, they
+ * always match with an ImageHost on the compositor thread. See
+ * CompositableClient.h for information on connecting clients to hosts.
+ */
+class ImageClient : public CompositableClient
+{
+public:
+ /**
+ * Creates, configures, and returns a new image client. If necessary, a
+ * message will be sent to the compositor to create a corresponding image
+ * host.
+ */
+ static already_AddRefed<ImageClient> CreateImageClient(CompositableType aImageHostType,
+ CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ virtual ~ImageClient() {}
+
+ /**
+ * Update this ImageClient from aContainer in aLayer
+ * returns false if this is the wrong kind of ImageClient for aContainer.
+ * Note that returning true does not necessarily imply success
+ */
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) = 0;
+
+ void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
+ ClientLayer* GetLayer() const { return mLayer; }
+
+ /**
+ * asynchronously remove all the textures used by the image client.
+ *
+ */
+ virtual void FlushAllImages() {}
+
+ virtual void RemoveTexture(TextureClient* aTexture) override;
+
+ virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
+
+ static already_AddRefed<TextureClient> CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder);
+
+protected:
+ ImageClient(CompositableForwarder* aFwd, TextureFlags aFlags,
+ CompositableType aType);
+
+ ClientLayer* mLayer;
+ CompositableType mType;
+ uint32_t mLastUpdateGenerationCounter;
+};
+
+/**
+ * An image client which uses a single texture client.
+ */
+class ImageClientSingle : public ImageClient
+{
+public:
+ ImageClientSingle(CompositableForwarder* aFwd,
+ TextureFlags aFlags,
+ CompositableType aType);
+
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+
+ virtual void OnDetach() override;
+
+ virtual bool AddTextureClient(TextureClient* aTexture) override;
+
+ virtual TextureInfo GetTextureInfo() const override;
+
+ virtual void FlushAllImages() override;
+
+ ImageClientSingle* AsImageClientSingle() override { return this; }
+
+protected:
+ struct Buffer {
+ RefPtr<TextureClient> mTextureClient;
+ int32_t mImageSerial;
+ };
+ nsTArray<Buffer> mBuffers;
+};
+
+/**
+ * Image class to be used for async image uploads using the image bridge
+ * protocol.
+ * We store the ImageBridge id in the TextureClientIdentifier.
+ */
+class ImageClientBridge : public ImageClient
+{
+public:
+ ImageClientBridge(CompositableForwarder* aFwd,
+ TextureFlags aFlags);
+
+ virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+ virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(mType);
+ }
+
+protected:
+ uint64_t mAsyncContainerID;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/SingleTiledContentClient.cpp b/gfx/layers/client/SingleTiledContentClient.cpp
new file mode 100644
index 000000000..bcc8691cf
--- /dev/null
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -0,0 +1,263 @@
+/* -*- 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/SingleTiledContentClient.h"
+
+#include "ClientTiledPaintedLayer.h"
+
+namespace mozilla {
+namespace layers {
+
+
+SingleTiledContentClient::SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager)
+ : TiledContentClient(aManager, "Single")
+{
+ MOZ_COUNT_CTOR(SingleTiledContentClient);
+
+ mTiledBuffer = new ClientSingleTiledLayerBuffer(aPaintedLayer, *this, aManager);
+}
+
+void
+SingleTiledContentClient::ClearCachedResources()
+{
+ CompositableClient::ClearCachedResources();
+ mTiledBuffer->DiscardBuffers();
+}
+
+void
+SingleTiledContentClient::UpdatedBuffer(TiledBufferType aType)
+{
+ mForwarder->UseTiledLayerBuffer(this, mTiledBuffer->GetSurfaceDescriptorTiles());
+ mTiledBuffer->ClearPaintedRegion();
+}
+
+/* static */ bool
+SingleTiledContentClient::ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager)
+{
+ int32_t maxTextureSize = aManager->GetMaxTextureSize();
+ return aSize.width <= maxTextureSize && aSize.height <= maxTextureSize;
+}
+
+ClientSingleTiledLayerBuffer::ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager)
+ : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
+ , mWasLastPaintProgressive(false)
+ , mFormat(gfx::SurfaceFormat::UNKNOWN)
+{
+}
+
+void
+ClientSingleTiledLayerBuffer::ReleaseTiles()
+{
+ if (!mTile.IsPlaceholderTile()) {
+ mTile.DiscardBuffers();
+ }
+ mTile.SetTextureAllocator(nullptr);
+}
+
+void
+ClientSingleTiledLayerBuffer::DiscardBuffers()
+{
+ if (!mTile.IsPlaceholderTile()) {
+ mTile.DiscardFrontBuffer();
+ mTile.DiscardBackBuffer();
+ }
+}
+
+SurfaceDescriptorTiles
+ClientSingleTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+ InfallibleTArray<TileDescriptor> tiles;
+
+ TileDescriptor tileDesc = mTile.GetTileDescriptor();
+ tiles.AppendElement(tileDesc);
+ mTile.mUpdateRect = gfx::IntRect();
+
+ return SurfaceDescriptorTiles(mValidRegion,
+ tiles,
+ mTilingOrigin,
+ mSize,
+ 0, 0, 1, 1,
+ 1.0,
+ mFrameResolution.xScale,
+ mFrameResolution.yScale,
+ mWasLastPaintProgressive);
+}
+
+already_AddRefed<TextureClient>
+ClientSingleTiledLayerBuffer::GetTextureClient()
+{
+ MOZ_ASSERT(mFormat != gfx::SurfaceFormat::UNKNOWN);
+ return mCompositableClient.CreateTextureClientForDrawing(
+ gfx::ImageFormatToSurfaceFormat(mFormat), mSize, BackendSelector::Content,
+ TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD);
+}
+
+void
+ClientSingleTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive)
+{
+ mWasLastPaintProgressive = aIsProgressive;
+
+ // Compare layer valid region size to current backbuffer size, discard if not matching.
+ gfx::IntSize size = aNewValidRegion.GetBounds().Size();
+ gfx::IntPoint origin = aNewValidRegion.GetBounds().TopLeft();
+ nsIntRegion paintRegion = aPaintRegion;
+
+ RefPtr<TextureClient> discardedFrontBuffer = nullptr;
+ RefPtr<TextureClient> discardedFrontBufferOnWhite = nullptr;
+ nsIntRegion discardedValidRegion;
+
+ if (mSize != size ||
+ mTilingOrigin != origin) {
+ discardedFrontBuffer = mTile.mFrontBuffer;
+ discardedFrontBufferOnWhite = mTile.mFrontBufferOnWhite;
+ discardedValidRegion = mValidRegion;
+
+ TILING_LOG("TILING %p: Single-tile valid region changed. Discarding buffers.\n", &mPaintedLayer)
+;
+ ResetPaintedAndValidState();
+ mSize = size;
+ mTilingOrigin = origin;
+ paintRegion = aNewValidRegion;
+ }
+
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ mFormat = gfxPlatform::GetPlatform()->OptimalFormatForContent(content);
+
+ if (mTile.IsPlaceholderTile()) {
+ mTile.SetTextureAllocator(this);
+ }
+
+ // The dirty region relative to the top-left of the tile.
+ nsIntRegion tileDirtyRegion = paintRegion.MovedBy(-mTilingOrigin);
+
+ nsIntRegion extraPainted;
+ RefPtr<TextureClient> backBufferOnWhite;
+ RefPtr<TextureClient> backBuffer =
+ mTile.GetBackBuffer(mCompositableClient,
+ tileDirtyRegion,
+ content, mode,
+ extraPainted,
+ &backBufferOnWhite);
+
+ mTile.mUpdateRect = tileDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
+ extraPainted.MoveBy(mTilingOrigin);
+ extraPainted.And(extraPainted, aNewValidRegion);
+ mPaintedRegion.OrWith(paintRegion);
+ mPaintedRegion.OrWith(extraPainted);
+
+ if (!backBuffer) {
+ return;
+ }
+
+ RefPtr<gfx::DrawTarget> dt = backBuffer->BorrowDrawTarget();
+ RefPtr<gfx::DrawTarget> dtOnWhite;
+ if (backBufferOnWhite) {
+ dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+ }
+
+ if (mode != SurfaceMode::SURFACE_OPAQUE) {
+ for (auto iter = tileDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect& rect = iter.Get();
+ if (dtOnWhite) {
+ dt->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
+ gfx::ColorPattern(gfx::Color(0.0, 0.0, 0.0, 1.0)));
+ dtOnWhite->FillRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height),
+ gfx::ColorPattern(gfx::Color(1.0, 1.0, 1.0, 1.0)));
+ } else {
+ dt->ClearRect(gfx::Rect(rect.x, rect.y, rect.width, rect.height));
+ }
+ }
+ }
+
+ // If the old frontbuffer was discarded then attempt to copy what we
+ // can from it to the new backbuffer.
+ if (discardedFrontBuffer) {
+ nsIntRegion copyableRegion;
+ copyableRegion.And(aNewValidRegion, discardedValidRegion);
+ copyableRegion.SubOut(aDirtyRegion);
+
+ if (!copyableRegion.IsEmpty()) {
+ TextureClientAutoLock frontLock(discardedFrontBuffer,
+ OpenMode::OPEN_READ);
+ if (frontLock.Succeeded()) {
+ for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+ const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+ discardedFrontBuffer->CopyToTextureClient(backBuffer, &rect, &dest);
+ }
+ }
+
+ if (discardedFrontBufferOnWhite && backBufferOnWhite) {
+ TextureClientAutoLock frontOnWhiteLock(discardedFrontBufferOnWhite,
+ OpenMode::OPEN_READ);
+ if (frontOnWhiteLock.Succeeded()) {
+ for (auto iter = copyableRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const gfx::IntRect rect = iter.Get() - discardedValidRegion.GetBounds().TopLeft();
+ const gfx::IntPoint dest = iter.Get().TopLeft() - mTilingOrigin;
+
+ discardedFrontBufferOnWhite->CopyToTextureClient(backBufferOnWhite,
+ &rect, &dest);
+ }
+ }
+ }
+
+ TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
+
+ // We don't need to repaint valid content that was just copied.
+ paintRegion.SubOut(copyableRegion);
+ }
+ }
+
+ if (dtOnWhite) {
+ dt = gfx::Factory::CreateDualDrawTarget(dt, dtOnWhite);
+ dtOnWhite = nullptr;
+ }
+
+ {
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
+ if (!ctx) {
+ gfxDevCrash(gfx::LogReason::InvalidContext) << "SingleTiledContextClient context problem " << gfx::hexa(dt);
+ return;
+ }
+ ctx->SetMatrix(ctx->CurrentMatrix().Translate(-mTilingOrigin.x, -mTilingOrigin.y));
+
+ aCallback(&mPaintedLayer, ctx, paintRegion, paintRegion, DrawRegionClip::DRAW, nsIntRegion(), aCallbackData);
+ }
+
+ // Mark the area we just drew into the back buffer as invalid in the front buffer as they're
+ // now out of sync.
+ mTile.mInvalidFront.OrWith(tileDirtyRegion);
+
+ // The new buffer is now validated, remove the dirty region from it.
+ mTile.mInvalidBack.SubOut(tileDirtyRegion);
+
+ dt = nullptr;
+
+ mTile.Flip();
+ UnlockTile(mTile);
+
+ if (backBuffer->HasIntermediateBuffer()) {
+ // If our new buffer has an internal buffer, we don't want to keep another
+ // TextureClient around unnecessarily, so discard the back-buffer.
+ mTile.DiscardBackBuffer();
+ }
+
+ mValidRegion = aNewValidRegion;
+ mLastPaintSurfaceMode = mode;
+ mLastPaintContentType = content;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/SingleTiledContentClient.h b/gfx/layers/client/SingleTiledContentClient.h
new file mode 100644
index 000000000..e1706bd90
--- /dev/null
+++ b/gfx/layers/client/SingleTiledContentClient.h
@@ -0,0 +1,135 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
+#define MOZILLA_GFX_SINGLETILEDCONTENTCLIENT_H
+
+#include "TiledContentClient.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledPaintedLayer;
+class ClientLayerManager;
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
+ * This buffer provides an implementation of ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer.
+ * Whether a single paint buffer is used is controlled by
+ * gfxPrefs::PerTileDrawing().
+ */
+class ClientSingleTiledLayerBuffer
+ : public ClientTiledLayerBuffer
+ , public TextureClientAllocator
+{
+ virtual ~ClientSingleTiledLayerBuffer() {}
+public:
+ ClientSingleTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager);
+
+ // TextureClientAllocator
+ already_AddRefed<TextureClient> GetTextureClient() override;
+ void ReturnTextureClientDeferred(TextureClient* aClient) override {}
+ void ReportClientLost() override {}
+
+ // ClientTiledLayerBuffer
+ void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) override;
+
+ bool SupportsProgressiveUpdate() override { return false; }
+ bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override
+ {
+ MOZ_ASSERT(false, "ProgressiveUpdate not supported!");
+ return false;
+ }
+
+ void ResetPaintedAndValidState() override {
+ mPaintedRegion.SetEmpty();
+ mValidRegion.SetEmpty();
+ mTile.DiscardBuffers();
+ }
+
+ const nsIntRegion& GetValidRegion() override {
+ return mValidRegion;
+ }
+
+ bool IsLowPrecision() const override {
+ return false;
+ }
+
+ void ReleaseTiles();
+
+ void DiscardBuffers();
+
+ SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+ void ClearPaintedRegion() {
+ mPaintedRegion.SetEmpty();
+ }
+
+private:
+ TileClient mTile;
+
+ nsIntRegion mPaintedRegion;
+ nsIntRegion mValidRegion;
+ bool mWasLastPaintProgressive;
+
+ /**
+ * While we're adding tiles, this is used to keep track of the position of
+ * the top-left of the top-left-most tile. When we come to wrap the tiles in
+ * TiledDrawTarget we subtract the value of this member from each tile's
+ * offset so that all the tiles have a positive offset, then add a
+ * translation to the TiledDrawTarget to compensate. This is important so
+ * that the mRect of the TiledDrawTarget is always at a positive x/y
+ * position, otherwise its GetSize() methods will be broken.
+ */
+ gfx::IntPoint mTilingOrigin;
+ gfx::IntSize mSize;
+ gfxImageFormat mFormat;
+};
+
+class SingleTiledContentClient : public TiledContentClient
+{
+public:
+ SingleTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager);
+
+protected:
+ ~SingleTiledContentClient()
+ {
+ MOZ_COUNT_DTOR(SingleTiledContentClient);
+
+ mTiledBuffer->ReleaseTiles();
+ }
+
+public:
+ static bool ClientSupportsLayerSize(const gfx::IntSize& aSize, ClientLayerManager* aManager);
+
+ virtual void ClearCachedResources() override;
+
+ virtual void UpdatedBuffer(TiledBufferType aType) override;
+
+ virtual ClientTiledLayerBuffer* GetTiledBuffer() override { return mTiledBuffer; }
+ virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override { return nullptr; }
+
+private:
+ RefPtr<ClientSingleTiledLayerBuffer> mTiledBuffer;
+};
+
+}
+}
+
+#endif
diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp
new file mode 100644
index 000000000..7182731bd
--- /dev/null
+++ b/gfx/layers/client/TextureClient.cpp
@@ -0,0 +1,1707 @@
+/* -*- 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/TextureClient.h"
+#include <stdint.h> // for uint8_t, uint32_t, etc
+#include "Layers.h" // for Layer, etc
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h" // for gfxPlatform
+#include "mozilla/Atomics.h"
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/Mutex.h"
+#include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc
+#include "ImageContainer.h" // for PlanarYCbCrData, etc
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/TextureClientOGL.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h" // for CreateDataSourceSurfaceByCloning
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "LayersLogging.h" // for AppendToString
+#include "gfxUtils.h" // for gfxUtils::GetAsLZ4Base64Str
+#include "IPDLActor.h"
+#include "BufferTexture.h"
+#include "gfxPrefs.h"
+#include "mozilla/layers/ShadowLayers.h"
+
+#ifdef XP_WIN
+#include "DeviceManagerD3D9.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/TextureD3D9.h"
+#include "mozilla/layers/TextureD3D11.h"
+#include "mozilla/layers/TextureDIB.h"
+#include "gfxWindowsPlatform.h"
+#include "gfx2DGlue.h"
+#endif
+#ifdef MOZ_X11
+#include "mozilla/layers/TextureClientX11.h"
+#ifdef GL_PROVIDER_GLX
+#include "GLXLibrary.h"
+#endif
+#endif
+
+#ifdef XP_MACOSX
+#include "mozilla/layers/MacIOSurfaceTextureClientOGL.h"
+#endif
+
+#if 0
+#define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+#define RECYCLE_LOG(...) do { } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::ipc;
+using namespace mozilla::gl;
+using namespace mozilla::gfx;
+
+struct TextureDeallocParams
+{
+ TextureData* data;
+ RefPtr<TextureChild> actor;
+ RefPtr<LayersIPCChannel> allocator;
+ bool clientDeallocation;
+ bool syncDeallocation;
+ bool workAroundSharedSurfaceOwnershipIssue;
+};
+
+void DeallocateTextureClient(TextureDeallocParams params);
+
+/**
+ * TextureChild is the content-side incarnation of the PTexture IPDL actor.
+ *
+ * TextureChild is used to synchronize a texture client and its corresponding
+ * TextureHost if needed (a TextureClient that is not shared with the compositor
+ * does not have a TextureChild)
+ *
+ * During the deallocation phase, a TextureChild may hold its recently destroyed
+ * TextureClient's data until the compositor side confirmed that it is safe to
+ * deallocte or recycle the it.
+ */
+class TextureChild final : PTextureChild
+{
+ ~TextureChild()
+ {
+ // We should have deallocated mTextureData in ActorDestroy
+ MOZ_ASSERT(!mTextureData);
+ MOZ_ASSERT_IF(!mOwnerCalledDestroy, !mTextureClient);
+ }
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
+
+ TextureChild()
+ : mCompositableForwarder(nullptr)
+ , mTextureForwarder(nullptr)
+ , mTextureClient(nullptr)
+ , mTextureData(nullptr)
+ , mDestroyed(false)
+ , mMainThreadOnly(false)
+ , mIPCOpen(false)
+ , mOwnsTextureData(false)
+ , mOwnerCalledDestroy(false)
+ {}
+
+ bool Recv__delete__() override { return true; }
+
+ LayersIPCChannel* GetAllocator() { return mTextureForwarder; }
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ bool IPCOpen() const { return mIPCOpen; }
+
+ void Lock() const { if (mCompositableForwarder && mCompositableForwarder->GetTextureForwarder()->UsesImageBridge()) { mLock.Enter(); } }
+
+ void Unlock() const { if (mCompositableForwarder && mCompositableForwarder->GetTextureForwarder()->UsesImageBridge()) { mLock.Leave(); } }
+
+private:
+
+ // AddIPDLReference and ReleaseIPDLReference are only to be called by CreateIPDLActor
+ // and DestroyIPDLActor, respectively. We intentionally make them private to prevent misuse.
+ // The purpose of these methods is to be aware of when the IPC system around this
+ // actor goes down: mIPCOpen is then set to false.
+ void AddIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == false);
+ mIPCOpen = true;
+ AddRef();
+ }
+ void ReleaseIPDLReference() {
+ MOZ_ASSERT(mIPCOpen == true);
+ mIPCOpen = false;
+ Release();
+ }
+
+ /// The normal way to destroy the actor.
+ ///
+ /// This will asynchronously send a Destroy message to the parent actor, whom
+ /// will send the delete message.
+ void Destroy(const TextureDeallocParams& aParams);
+
+ /// The ugly and slow way to destroy the actor.
+ ///
+ /// This will block until the Parent actor has handled the Destroy message,
+ /// and then start the asynchronous handshake (and destruction will already
+ /// be done on the parent side, when the async part happens).
+ void DestroySynchronously(const TextureDeallocParams& aParams);
+
+ // This lock is used order to prevent several threads to access the
+ // TextureClient's data concurrently. In particular, it prevents shutdown
+ // code to destroy a texture while another thread is reading or writing into
+ // it.
+ // In most places, the lock is held in short and bounded scopes in which we
+ // don't block on any other resource. There are few exceptions to this, which
+ // are discussed below.
+ //
+ // The locking pattern of TextureClient may in some case upset deadlock detection
+ // tools such as TSan.
+ // Typically our tile rendering code will lock all of its tiles, render into them
+ // and unlock them all right after that, which looks something like:
+ //
+ // Lock tile A
+ // Lock tile B
+ // Lock tile C
+ // Apply drawing commands to tiles A, B and C
+ // Unlock tile A
+ // Unlock tile B
+ // Unlock tile C
+ //
+ // And later, we may end up rendering a tile buffer that has the same tiles,
+ // in a different order, for example:
+ //
+ // Lock tile B
+ // Lock tile A
+ // Lock tile D
+ // Apply drawing commands to tiles A, B and D
+ // Unlock tile B
+ // Unlock tile A
+ // Unlock tile D
+ //
+ // This is because textures being expensive to create, we recycle them as much
+ // as possible and they may reappear in the tile buffer in a different order.
+ //
+ // Unfortunately this is not very friendly to TSan's analysis, which will see
+ // that B was once locked while A was locked, and then A locked while B was
+ // locked. TSan identifies this as a potential dead-lock which would be the
+ // case if this kind of inconsistent and dependent locking order was happening
+ // concurrently.
+ // In the case of TextureClient, dependent locking only ever happens on the
+ // thread that draws into the texture (let's call it the producer thread). Other
+ // threads may call into a method that can lock the texture in a short and
+ // bounded scope inside of which it is not allowed to do anything that could
+ // cause the thread to block. A given texture can only have one producer thread.
+ //
+ // Another example of TSan-unfriendly locking pattern is when copying a texture
+ // into another, which also never happens outside of the producer thread.
+ // Copying A into B looks like this:
+ //
+ // Lock texture B
+ // Lock texture A
+ // Copy A into B
+ // Unlock A
+ // Unlock B
+ //
+ // In a given frame we may need to copy A into B and in another frame copy
+ // B into A. For example A and B can be the Front and Back buffers, alternating
+ // roles and the copy is needed to avoid the cost of re-drawing the valid
+ // region.
+ //
+ // The important rule is that all of the dependent locking must occur only
+ // in the texture's producer thread to avoid deadlocks.
+ mutable gfx::CriticalSection mLock;
+
+ RefPtr<CompositableForwarder> mCompositableForwarder;
+ RefPtr<TextureForwarder> mTextureForwarder;
+
+ TextureClient* mTextureClient;
+ TextureData* mTextureData;
+ Atomic<bool> mDestroyed;
+ bool mMainThreadOnly;
+ bool mIPCOpen;
+ bool mOwnsTextureData;
+ bool mOwnerCalledDestroy;
+
+ friend class TextureClient;
+ friend void DeallocateTextureClient(TextureDeallocParams params);
+};
+
+
+static void DestroyTextureData(TextureData* aTextureData, LayersIPCChannel* aAllocator,
+ bool aDeallocate, bool aMainThreadOnly)
+{
+ if (!aTextureData) {
+ return;
+ }
+
+ if (aMainThreadOnly && !NS_IsMainThread()) {
+ RefPtr<LayersIPCChannel> allocatorRef = aAllocator;
+ NS_DispatchToMainThread(NS_NewRunnableFunction([aTextureData, allocatorRef, aDeallocate]() -> void {
+ DestroyTextureData(aTextureData, allocatorRef, aDeallocate, true);
+ }));
+ return;
+ }
+
+ if (aDeallocate) {
+ aTextureData->Deallocate(aAllocator);
+ } else {
+ aTextureData->Forget(aAllocator);
+ }
+ delete aTextureData;
+}
+
+void
+TextureChild::ActorDestroy(ActorDestroyReason why)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
+
+ if (mTextureData) {
+ DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData, mMainThreadOnly);
+ mTextureData = nullptr;
+ }
+}
+
+void
+TextureChild::Destroy(const TextureDeallocParams& aParams)
+{
+ MOZ_ASSERT(!mOwnerCalledDestroy);
+ if (mOwnerCalledDestroy) {
+ return;
+ }
+
+ mOwnerCalledDestroy = true;
+
+ // DestroyTextureData will be called by TextureChild::ActorDestroy
+ mTextureData = aParams.data;
+ mOwnsTextureData = aParams.clientDeallocation;
+
+ if (!mCompositableForwarder ||
+ !mCompositableForwarder->DestroyInTransaction(this, false))
+ {
+ this->SendDestroy();
+ }
+}
+
+void
+TextureChild::DestroySynchronously(const TextureDeallocParams& aParams)
+{
+ MOZ_PERFORMANCE_WARNING("gfx", "TextureClient/Host pair requires synchronous deallocation");
+
+ MOZ_ASSERT(!mOwnerCalledDestroy);
+ if (mOwnerCalledDestroy) {
+ return;
+ }
+
+ mOwnerCalledDestroy = true;
+
+ DestroyTextureData(
+ aParams.data,
+ aParams.allocator,
+ aParams.clientDeallocation,
+ mMainThreadOnly);
+
+ if (!IPCOpen()) {
+ return;
+ }
+
+ if (!mCompositableForwarder ||
+ !mCompositableForwarder->DestroyInTransaction(this, true))
+ {
+ this->SendDestroySync();
+ this->SendDestroy();
+ }
+}
+
+/* static */ Atomic<uint64_t> TextureClient::sSerialCounter(0);
+
+void DeallocateTextureClientSyncProxy(TextureDeallocParams params,
+ ReentrantMonitor* aBarrier, bool* aDone)
+{
+ DeallocateTextureClient(params);
+ ReentrantMonitorAutoEnter autoMon(*aBarrier);
+ *aDone = true;
+ aBarrier->NotifyAll();
+}
+
+/// The logic for synchronizing a TextureClient's deallocation goes here.
+///
+/// This funciton takes care of dispatching work to the right thread using
+/// a synchronous proxy if needed, and handles client/host deallocation.
+void
+DeallocateTextureClient(TextureDeallocParams params)
+{
+ if (!params.actor && !params.data) {
+ // Nothing to do
+ return;
+ }
+
+ TextureChild* actor = params.actor;
+ MessageLoop* ipdlMsgLoop = nullptr;
+
+ if (params.allocator) {
+ ipdlMsgLoop = params.allocator->GetMessageLoop();
+ if (!ipdlMsgLoop) {
+ // An allocator with no message loop means we are too late in the shutdown
+ // sequence.
+ gfxCriticalError() << "Texture deallocated too late during shutdown";
+ return;
+ }
+ }
+
+ // First make sure that the work is happening on the IPDL thread.
+ if (ipdlMsgLoop && MessageLoop::current() != ipdlMsgLoop) {
+ if (params.syncDeallocation) {
+ bool done = false;
+ ReentrantMonitor barrier("DeallocateTextureClient");
+ ReentrantMonitorAutoEnter autoMon(barrier);
+ ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClientSyncProxy,
+ params, &barrier, &done));
+ while (!done) {
+ barrier.Wait();
+ }
+ } else {
+ ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClient,
+ params));
+ }
+ // The work has been forwarded to the IPDL thread, we are done.
+ return;
+ }
+
+ // Below this line, we are either in the IPDL thread or ther is no IPDL
+ // thread anymore.
+
+ if (!ipdlMsgLoop) {
+ // If we don't have a message loop we can't know for sure that we are in
+ // the IPDL thread and use the LayersIPCChannel.
+ // This should ideally not happen outside of gtest, but some shutdown raciness
+ // could put us in this situation.
+ params.allocator = nullptr;
+ }
+
+ if (!actor) {
+ // We don't have an IPDL actor, probably because we destroyed the TextureClient
+ // before sharing it with the compositor. It means the data cannot be owned by
+ // the TextureHost since we never created the TextureHost...
+ // ..except if the lovely mWorkaroundAnnoyingSharedSurfaceOwnershipIssues member
+ // is set to true. In this case we are in a special situation where this
+ // TextureClient is in wrapped into another TextureClient which assumes it owns
+ // our data. This is specific to the gralloc SharedSurface.
+ bool shouldDeallocate = !params.workAroundSharedSurfaceOwnershipIssue;
+ DestroyTextureData(params.data, params.allocator,
+ shouldDeallocate,
+ false); // main-thread deallocation
+ return;
+ }
+
+ if (params.syncDeallocation || !actor->IPCOpen()) {
+ actor->DestroySynchronously(params);
+ } else {
+ actor->Destroy(params);
+ }
+}
+
+void TextureClient::Destroy(bool aForceSync)
+{
+ if (mActor && !mIsLocked) {
+ mActor->Lock();
+ }
+
+ mBorrowedDrawTarget = nullptr;
+ mReadLock = nullptr;
+
+ RefPtr<TextureChild> actor = mActor;
+ mActor = nullptr;
+
+ if (actor && !actor->mDestroyed.compareExchange(false, true)) {
+ actor->Unlock();
+ actor = nullptr;
+ }
+
+ TextureData* data = mData;
+ if (!mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+ mData = nullptr;
+ }
+
+ if (data || actor) {
+ TextureDeallocParams params;
+ params.actor = actor;
+ params.allocator = mAllocator;
+ params.clientDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
+ params.workAroundSharedSurfaceOwnershipIssue = mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
+ if (mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+ params.data = nullptr;
+ } else {
+ params.data = data;
+ }
+ // At the moment we always deallocate synchronously when deallocating on the
+ // client side, but having asynchronous deallocate in some of the cases will
+ // be a worthwhile optimization.
+ params.syncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT) || aForceSync;
+
+ // Release the lock before calling DeallocateTextureClient because the latter
+ // may wait for the main thread which could create a dead-lock.
+
+ if (actor) {
+ actor->Unlock();
+ }
+
+ DeallocateTextureClient(params);
+ }
+}
+
+void
+TextureClient::LockActor() const
+{
+ if (mActor) {
+ mActor->Lock();
+ }
+}
+
+void
+TextureClient::UnlockActor() const
+{
+ if (mActor) {
+ mActor->Unlock();
+ }
+}
+
+bool
+TextureClient::IsReadLocked() const
+{
+ return mReadLock && mReadLock->GetReadCount() > 1;
+}
+
+bool
+TextureClient::Lock(OpenMode aMode)
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(!mIsLocked);
+ if (!IsValid()) {
+ return false;
+ }
+ if (mIsLocked) {
+ return mOpenMode == aMode;
+ }
+
+ if (aMode & OpenMode::OPEN_WRITE && IsReadLocked()) {
+ NS_WARNING("Attempt to Lock a texture that is being read by the compositor!");
+ return false;
+ }
+
+ LockActor();
+
+ mIsLocked = mData->Lock(aMode);
+ mOpenMode = aMode;
+
+ auto format = GetFormat();
+ if (mIsLocked && CanExposeDrawTarget() &&
+ aMode == OpenMode::OPEN_READ_WRITE &&
+ NS_IsMainThread() &&
+ // the formats that we apparently expect, in the cairo backend. Any other
+ // format will trigger an assertion in GfxFormatToCairoFormat.
+ (format == SurfaceFormat::A8R8G8B8_UINT32 ||
+ format == SurfaceFormat::X8R8G8B8_UINT32 ||
+ format == SurfaceFormat::A8 ||
+ format == SurfaceFormat::R5G6B5_UINT16)) {
+ if (!BorrowDrawTarget()) {
+ // Failed to get a DrawTarget, means we won't be able to write into the
+ // texture, might as well fail now.
+ Unlock();
+ return false;
+ }
+ }
+
+ if (!mIsLocked) {
+ UnlockActor();
+ }
+
+ return mIsLocked;
+}
+
+void
+TextureClient::Unlock()
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ if (!IsValid() || !mIsLocked) {
+ return;
+ }
+
+ if (mBorrowedDrawTarget) {
+ if (mOpenMode & OpenMode::OPEN_WRITE) {
+ mBorrowedDrawTarget->Flush();
+ if (mReadbackSink && !mData->ReadBack(mReadbackSink)) {
+ // Fallback implementation for reading back, because mData does not
+ // have a backend-specific implementation and returned false.
+ RefPtr<SourceSurface> snapshot = mBorrowedDrawTarget->Snapshot();
+ RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
+ mReadbackSink->ProcessReadback(dataSurf);
+ }
+ }
+
+ mBorrowedDrawTarget->DetachAllSnapshots();
+ // If this assertion is hit, it means something is holding a strong reference
+ // to our DrawTarget externally, which is not allowed.
+ MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
+
+ mBorrowedDrawTarget = nullptr;
+ }
+
+ if (mOpenMode & OpenMode::OPEN_WRITE) {
+ mUpdated = true;
+ }
+
+ if (mData) {
+ mData->Unlock();
+ }
+ mIsLocked = false;
+ mOpenMode = OpenMode::OPEN_NONE;
+
+ UnlockActor();
+}
+
+void
+TextureClient::EnableReadLock()
+{
+ if (!mReadLock) {
+ mReadLock = TextureReadLock::Create(mAllocator);
+ }
+}
+
+void
+TextureClient::SerializeReadLock(ReadLockDescriptor& aDescriptor)
+{
+ if (mReadLock && mUpdated) {
+ // Take a read lock on behalf of the TextureHost. The latter will unlock
+ // after the shared data is available again for drawing.
+ mReadLock->ReadLock();
+ mReadLock->Serialize(aDescriptor);
+ mUpdated = false;
+ } else {
+ aDescriptor = null_t();
+ }
+}
+
+TextureClient::~TextureClient()
+{
+ mReadLock = nullptr;
+ Destroy(false);
+}
+
+void
+TextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ MOZ_ASSERT(aSurface);
+ // If you run into this assertion, make sure the texture was locked write-only
+ // rather than read-write.
+ MOZ_ASSERT(!mBorrowedDrawTarget);
+
+ // XXX - It would be better to first try the DrawTarget approach and fallback
+ // to the backend-specific implementation because the latter will usually do
+ // an expensive read-back + cpu-side copy if the texture is on the gpu.
+ // There is a bug with the DrawTarget approach, though specific to reading back
+ // from WebGL (where R and B channel end up inverted) to figure out first.
+ if (mData->UpdateFromSurface(aSurface)) {
+ return;
+ }
+ if (CanExposeDrawTarget() && NS_IsMainThread()) {
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+
+ MOZ_ASSERT(dt);
+ if (dt) {
+ dt->CopySurface(aSurface,
+ gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()),
+ gfx::IntPoint(0, 0));
+ return;
+ }
+ }
+ NS_WARNING("TextureClient::UpdateFromSurface failed");
+}
+
+
+already_AddRefed<TextureClient>
+TextureClient::CreateSimilar(LayersBackend aLayersBackend, TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const
+{
+ MOZ_ASSERT(IsValid());
+
+ MOZ_ASSERT(!mIsLocked);
+ if (mIsLocked) {
+ return nullptr;
+ }
+
+ LockActor();
+ TextureData* data = mData->CreateSimilar(mAllocator, aLayersBackend, aFlags, aAllocFlags);
+ UnlockActor();
+
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aFlags, mAllocator);
+}
+
+gfx::DrawTarget*
+TextureClient::BorrowDrawTarget()
+{
+ MOZ_ASSERT(IsValid());
+ MOZ_ASSERT(mIsLocked);
+ // TODO- We can't really assert that at the moment because there is code that Borrows
+ // the DrawTarget, just to get a snapshot, which is legit in term of OpenMode
+ // but we should have a way to get a SourceSurface directly instead.
+ //MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
+
+ if (!IsValid() || !mIsLocked) {
+ return nullptr;
+ }
+
+ if (!NS_IsMainThread()) {
+ return nullptr;
+ }
+
+ if (!mBorrowedDrawTarget) {
+ mBorrowedDrawTarget = mData->BorrowDrawTarget();
+#ifdef DEBUG
+ mExpectedDtRefs = mBorrowedDrawTarget ? mBorrowedDrawTarget->refCount() : 0;
+#endif
+ }
+
+ return mBorrowedDrawTarget;
+}
+
+bool
+TextureClient::BorrowMappedData(MappedTextureData& aMap)
+{
+ MOZ_ASSERT(IsValid());
+
+ // TODO - SharedRGBImage just accesses the buffer without properly locking
+ // the texture. It's bad.
+ //MOZ_ASSERT(mIsLocked);
+ //if (!mIsLocked) {
+ // return nullptr;
+ //}
+
+ return mData ? mData->BorrowMappedData(aMap) : false;
+}
+
+bool
+TextureClient::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+ MOZ_ASSERT(IsValid());
+
+ return mData ? mData->BorrowMappedYCbCrData(aMap) : false;
+}
+
+bool
+TextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
+{
+ MOZ_ASSERT(IsValid());
+
+ return mData ? mData->Serialize(aOutDescriptor) : false;
+}
+
+// static
+PTextureChild*
+TextureClient::CreateIPDLActor()
+{
+ TextureChild* c = new TextureChild();
+ c->AddIPDLReference();
+ return c;
+}
+
+// static
+bool
+TextureClient::DestroyIPDLActor(PTextureChild* actor)
+{
+ static_cast<TextureChild*>(actor)->ReleaseIPDLReference();
+ return true;
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::AsTextureClient(PTextureChild* actor)
+{
+ if (!actor) {
+ return nullptr;
+ }
+
+ TextureChild* tc = static_cast<TextureChild*>(actor);
+
+ tc->Lock();
+
+ // Since TextureClient may be destroyed asynchronously with respect to its
+ // IPDL actor, we must acquire a reference within a lock. The mDestroyed bit
+ // tells us whether or not the main thread has disconnected the TextureClient
+ // from its actor.
+ if (tc->mDestroyed) {
+ tc->Unlock();
+ return nullptr;
+ }
+
+ RefPtr<TextureClient> texture = tc->mTextureClient;
+ tc->Unlock();
+
+ return texture.forget();
+}
+
+bool
+TextureClient::IsSharedWithCompositor() const {
+ return mActor && mActor->IPCOpen();
+}
+
+void
+TextureClient::AddFlags(TextureFlags aFlags)
+{
+ MOZ_ASSERT(!IsSharedWithCompositor() ||
+ ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
+ mFlags |= aFlags;
+}
+
+void
+TextureClient::RemoveFlags(TextureFlags aFlags)
+{
+ MOZ_ASSERT(!IsSharedWithCompositor() ||
+ ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
+ mFlags &= ~aFlags;
+}
+
+void
+TextureClient::RecycleTexture(TextureFlags aFlags)
+{
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(!mIsLocked);
+
+ mAddedToCompositableClient = false;
+ if (mFlags != aFlags) {
+ mFlags = aFlags;
+ }
+}
+
+void
+TextureClient::SetAddedToCompositableClient()
+{
+ if (!mAddedToCompositableClient) {
+ mAddedToCompositableClient = true;
+ if(!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+ MOZ_ASSERT(!mIsLocked);
+ LockActor();
+ if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
+ mActor->SendRecycleTexture(mFlags);
+ }
+ UnlockActor();
+ }
+}
+
+void CancelTextureClientRecycle(uint64_t aTextureId, LayersIPCChannel* aAllocator)
+{
+ if (!aAllocator) {
+ return;
+ }
+ MessageLoop* msgLoop = nullptr;
+ msgLoop = aAllocator->GetMessageLoop();
+ if (!msgLoop) {
+ return;
+ }
+ if (MessageLoop::current() == msgLoop) {
+ aAllocator->CancelWaitForRecycle(aTextureId);
+ } else {
+ msgLoop->PostTask(NewRunnableFunction(CancelTextureClientRecycle,
+ aTextureId, aAllocator));
+ }
+}
+
+void
+TextureClient::CancelWaitForRecycle()
+{
+ if (GetFlags() & TextureFlags::RECYCLE) {
+ CancelTextureClientRecycle(mSerial, GetAllocator());
+ return;
+ }
+}
+
+/* static */ void
+TextureClient::TextureClientRecycleCallback(TextureClient* aClient, void* aClosure)
+{
+ MOZ_ASSERT(aClient->GetRecycleAllocator());
+ aClient->GetRecycleAllocator()->RecycleTextureClient(aClient);
+}
+
+void
+TextureClient::SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator)
+{
+ mRecycleAllocator = aAllocator;
+ if (aAllocator) {
+ SetRecycleCallback(TextureClientRecycleCallback, nullptr);
+ } else {
+ ClearRecycleCallback();
+ }
+}
+
+bool
+TextureClient::InitIPDLActor(CompositableForwarder* aForwarder)
+{
+ MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetMessageLoop() == mAllocator->GetMessageLoop());
+ if (mActor && !mActor->mDestroyed) {
+ CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
+ TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
+ if (currentFwd != aForwarder) {
+ // It's a bit iffy but right now ShadowLayerForwarder inherits TextureForwarder
+ // even though it should not. ShadowLayerForwarder::GetTextureForwarder actually
+ // returns a pointer to the CompositorBridgeChild.
+ // It's Ok for a texture to move from a ShadowLayerForwarder to another, but
+ // not form a CompositorBridgeChild to another (they use different channels).
+ if (currentTexFwd && currentTexFwd != aForwarder->GetTextureForwarder()) {
+ gfxCriticalError() << "Attempt to move a texture to a different channel CF.";
+ return false;
+ }
+ if (currentFwd && currentFwd->GetCompositorBackendType() != aForwarder->GetCompositorBackendType()) {
+ gfxCriticalError() << "Attempt to move a texture to different compositor backend.";
+ return false;
+ }
+ mActor->mCompositableForwarder = aForwarder;
+ }
+ return true;
+ }
+ MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+
+ SurfaceDescriptor desc;
+ if (!ToSurfaceDescriptor(desc)) {
+ return false;
+ }
+
+ PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture(
+ desc,
+ aForwarder->GetCompositorBackendType(),
+ GetFlags(),
+ mSerial);
+ if (!actor) {
+ gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
+ << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
+ << static_cast<uint32_t>(GetFlags())
+ << ", " << mSerial;
+ return false;
+ }
+
+ mActor = static_cast<TextureChild*>(actor);
+ mActor->mCompositableForwarder = aForwarder;
+ mActor->mTextureForwarder = aForwarder->GetTextureForwarder();
+ mActor->mTextureClient = this;
+ mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
+
+ // If the TextureClient is already locked, we have to lock TextureChild's mutex
+ // since it will be unlocked in TextureClient::Unlock.
+ if (mIsLocked) {
+ LockActor();
+ }
+
+ return mActor->IPCOpen();
+}
+
+bool
+TextureClient::InitIPDLActor(KnowsCompositor* aForwarder)
+{
+ MOZ_ASSERT(aForwarder && aForwarder->GetTextureForwarder()->GetMessageLoop() == mAllocator->GetMessageLoop());
+ TextureForwarder* fwd = aForwarder->GetTextureForwarder();
+ if (mActor && !mActor->mDestroyed) {
+ CompositableForwarder* currentFwd = mActor->mCompositableForwarder;
+ TextureForwarder* currentTexFwd = mActor->mTextureForwarder;
+
+ if (currentFwd) {
+ gfxCriticalError() << "Attempt to remove a texture from a CompositableForwarder.";
+ return false;
+ }
+
+ if (currentTexFwd && currentTexFwd != fwd) {
+ gfxCriticalError() << "Attempt to move a texture to a different channel TF.";
+ return false;
+ }
+ mActor->mTextureForwarder = fwd;
+ return true;
+ }
+ MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+
+ SurfaceDescriptor desc;
+ if (!ToSurfaceDescriptor(desc)) {
+ return false;
+ }
+
+ PTextureChild* actor = fwd->CreateTexture(
+ desc,
+ aForwarder->GetCompositorBackendType(),
+ GetFlags(),
+ mSerial);
+ if (!actor) {
+ gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
+ << static_cast<int32_t>(aForwarder->GetCompositorBackendType()) << ", "
+ << static_cast<uint32_t>(GetFlags())
+ << ", " << mSerial;
+ return false;
+ }
+
+ mActor = static_cast<TextureChild*>(actor);
+ mActor->mTextureForwarder = fwd;
+ mActor->mTextureClient = this;
+ mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
+
+ // If the TextureClient is already locked, we have to lock TextureChild's mutex
+ // since it will be unlocked in TextureClient::Unlock.
+ if (mIsLocked) {
+ LockActor();
+ }
+
+ return mActor->IPCOpen();
+}
+
+PTextureChild*
+TextureClient::GetIPDLActor()
+{
+ return mActor;
+}
+
+static inline gfx::BackendType
+BackendTypeForBackendSelector(LayersBackend aLayersBackend, BackendSelector aSelector)
+{
+ switch (aSelector) {
+ case BackendSelector::Canvas:
+ return gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
+ case BackendSelector::Content:
+ return gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown backend selector");
+ return gfx::BackendType::NONE;
+ }
+};
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForDrawing(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
+ return TextureClient::CreateForDrawing(aAllocator->GetTextureForwarder(),
+ aFormat, aSize,
+ layersBackend,
+ aAllocator->GetMaxTextureSize(),
+ aSelector,
+ aTextureFlags,
+ aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForDrawing(TextureForwarder* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(aLayersBackend, aSelector);
+
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ TextureData* data = nullptr;
+
+#ifdef XP_WIN
+ if (aLayersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == gfx::BackendType::DIRECT2D ||
+ moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
+ (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+ DeviceManagerDx::Get()->GetContentDevice())) &&
+ aSize.width <= aMaxTextureSize &&
+ aSize.height <= aMaxTextureSize &&
+ !(aAllocFlags & ALLOC_UPDATE_FROM_SURFACE))
+ {
+ data = DXGITextureData::Create(aSize, aFormat, aAllocFlags);
+ }
+ if (aLayersBackend == LayersBackend::LAYERS_D3D9 &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ aAllocator->IsSameProcess() &&
+ aSize.width <= aMaxTextureSize &&
+ aSize.height <= aMaxTextureSize &&
+ NS_IsMainThread() &&
+ DeviceManagerD3D9::GetDevice()) {
+ data = D3D9TextureData::Create(aSize, aFormat, aAllocFlags);
+ }
+
+ if (!data && aFormat == SurfaceFormat::B8G8R8X8 &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ NS_IsMainThread()) {
+ data = DIBTextureData::Create(aSize, aFormat, aAllocator);
+ }
+#endif
+
+#ifdef MOZ_X11
+ gfxSurfaceType type =
+ gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
+
+ if (!data && aLayersBackend == LayersBackend::LAYERS_BASIC &&
+ moz2DBackend == gfx::BackendType::CAIRO &&
+ type == gfxSurfaceType::Xlib)
+ {
+ data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+ }
+#ifdef GL_PROVIDER_GLX
+ if (!data && aLayersBackend == LayersBackend::LAYERS_OPENGL &&
+ type == gfxSurfaceType::Xlib &&
+ aFormat != SurfaceFormat::A8 &&
+ gl::sGLXLibrary.UseTextureFromPixmap())
+ {
+ data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
+ }
+#endif
+#endif
+
+#ifdef XP_MACOSX
+ if (!data && gfxPrefs::UseIOSurfaceTextures()) {
+ data = MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator);
+ }
+
+ if (moz2DBackend == BackendType::SKIA && aFormat == SurfaceFormat::B8G8R8X8) {
+ // Skia doesn't support RGBX, so ensure we clear the buffer for the proper alpha values.
+ aAllocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_CLEAR_BUFFER);
+ }
+
+ // Can't do any better than a buffer texture client.
+ return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,
+ moz2DBackend, aLayersBackend,
+ aTextureFlags, aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateFromSurface(KnowsCompositor* aAllocator,
+ gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->GetTextureForwarder()->IPCOpen()) {
+ return nullptr;
+ }
+
+ gfx::IntSize size = aSurface->GetSize();
+
+ if (!gfx::Factory::AllowedSurfaceSize(size)) {
+ return nullptr;
+ }
+
+ TextureData* data = nullptr;
+#if defined(XP_WIN)
+ LayersBackend layersBackend = aAllocator->GetCompositorBackendType();
+ gfx::BackendType moz2DBackend = BackendTypeForBackendSelector(layersBackend, aSelector);
+
+ int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
+
+ if (layersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == gfx::BackendType::DIRECT2D ||
+ moz2DBackend == gfx::BackendType::DIRECT2D1_1 ||
+ (!!(aAllocFlags & ALLOC_FOR_OUT_OF_BAND_CONTENT) &&
+ DeviceManagerDx::Get()->GetContentDevice())) &&
+ size.width <= maxTextureSize &&
+ size.height <= maxTextureSize)
+ {
+ data = D3D11TextureData::Create(aSurface, aAllocFlags);
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags, aAllocator->GetTextureForwarder());
+ }
+
+ // Fall back to using UpdateFromSurface
+
+ TextureAllocationFlags allocFlags = TextureAllocationFlags(aAllocFlags | ALLOC_UPDATE_FROM_SURFACE);
+ RefPtr<TextureClient> client = CreateForDrawing(aAllocator, aSurface->GetFormat(), size,
+ aSelector, aTextureFlags, allocFlags);
+ if (!client) {
+ return nullptr;
+ }
+
+ TextureClientAutoLock autoLock(client, OpenMode::OPEN_WRITE_ONLY);
+ if (!autoLock.Succeeded()) {
+ return nullptr;
+ }
+
+ client->UpdateFromSurface(aSurface);
+ return client.forget();
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForRawBufferAccess(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return CreateForRawBufferAccess(aAllocator->GetTextureForwarder(),
+ aFormat, aSize, aMoz2DBackend,
+ aAllocator->GetCompositorBackendType(),
+ aTextureFlags, aAllocFlags);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2DBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ // also test the validity of aAllocator
+ if (!aAllocator || !aAllocator->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (aAllocFlags & ALLOC_DISALLOW_BUFFERTEXTURECLIENT) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
+ return nullptr;
+ }
+
+ // D2D backend does not support CreateDrawTargetForData(). Use CAIRO instead.
+ if (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
+ aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) {
+ aMoz2DBackend = gfx::BackendType::CAIRO;
+ }
+
+ TextureData* texData = BufferTextureData::Create(aSize, aFormat, aMoz2DBackend,
+ aLayersBackend, aTextureFlags,
+ aAllocFlags, aAllocator);
+ if (!texData) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(texData, aTextureFlags, aAllocator);
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
+ return nullptr;
+ }
+
+ if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
+ return nullptr;
+ }
+
+ TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, aYSize, aCbCrSize,
+ aStereoMode, aYUVColorSpace,
+ aTextureFlags);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ aAllocator->GetTextureForwarder());
+}
+
+// static
+already_AddRefed<TextureClient>
+TextureClient::CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ size_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags)
+{
+ if (!aAllocator || !aAllocator->GetLayersIPCActor()->IPCOpen()) {
+ return nullptr;
+ }
+
+ TextureData* data =
+ BufferTextureData::CreateForYCbCrWithBufferSize(aAllocator, aSize, aYUVColorSpace,
+ aTextureFlags);
+ if (!data) {
+ return nullptr;
+ }
+
+ return MakeAndAddRef<TextureClient>(data, aTextureFlags,
+ aAllocator->GetTextureForwarder());
+}
+
+TextureClient::TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
+: AtomicRefCountedWithFinalize("TextureClient")
+, mAllocator(aAllocator)
+, mActor(nullptr)
+, mData(aData)
+, mFlags(aFlags)
+, mOpenMode(OpenMode::OPEN_NONE)
+#ifdef DEBUG
+, mExpectedDtRefs(0)
+#endif
+, mIsLocked(false)
+, mUpdated(false)
+, mAddedToCompositableClient(false)
+, mWorkaroundAnnoyingSharedSurfaceLifetimeIssues(false)
+, mWorkaroundAnnoyingSharedSurfaceOwnershipIssues(false)
+, mFwdTransactionId(0)
+, mSerial(++sSerialCounter)
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+, mPoolTracker(nullptr)
+#endif
+{
+ mData->FillInfo(mInfo);
+ mFlags |= mData->GetTextureFlags();
+}
+
+bool TextureClient::CopyToTextureClient(TextureClient* aTarget,
+ const gfx::IntRect* aRect,
+ const gfx::IntPoint* aPoint)
+{
+ MOZ_ASSERT(IsLocked());
+ MOZ_ASSERT(aTarget->IsLocked());
+
+ if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) {
+ return false;
+ }
+
+ RefPtr<DrawTarget> destinationTarget = aTarget->BorrowDrawTarget();
+ if (!destinationTarget) {
+ gfxWarning() << "TextureClient::CopyToTextureClient (dest) failed in BorrowDrawTarget";
+ return false;
+ }
+
+ RefPtr<DrawTarget> sourceTarget = BorrowDrawTarget();
+ if (!sourceTarget) {
+ gfxWarning() << "TextureClient::CopyToTextureClient (src) failed in BorrowDrawTarget";
+ return false;
+ }
+
+ RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
+ destinationTarget->CopySurface(source,
+ aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
+ aPoint ? *aPoint : gfx::IntPoint(0, 0));
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+TextureClient::GetAsSurface()
+{
+ if (!Lock(OpenMode::OPEN_READ)) {
+ return nullptr;
+ }
+ RefPtr<gfx::DataSourceSurface> data;
+ { // scope so that the DrawTarget is destroyed before Unlock()
+ RefPtr<gfx::DrawTarget> dt = BorrowDrawTarget();
+ if (dt) {
+ RefPtr<gfx::SourceSurface> surf = dt->Snapshot();
+ if (surf) {
+ data = surf->GetDataSurface();
+ }
+ }
+ }
+ Unlock();
+ return data.forget();
+}
+
+void
+TextureClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("TextureClient (0x%p)", this).get();
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ AppendToString(aStream, mFlags, " [flags=", "]");
+
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+class MemoryTextureReadLock : public TextureReadLock {
+public:
+ MemoryTextureReadLock();
+
+ ~MemoryTextureReadLock();
+
+ virtual int32_t ReadLock() override;
+
+ virtual int32_t ReadUnlock() override;
+
+ virtual int32_t GetReadCount() override;
+
+ virtual LockType GetType() override { return TYPE_MEMORY; }
+
+ virtual bool IsValid() const override { return true; };
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) override;
+
+ int32_t mReadCount;
+};
+
+// The cross-prcess implementation of TextureReadLock.
+//
+// Since we don't use cross-process reference counting for the ReadLock objects,
+// we use the lock's internal counter as a way to know when to deallocate the
+// underlying shmem section: when the counter is equal to 1, it means that the
+// lock is not "held" (the texture is writable), when the counter is equal to 0
+// it means that we can safely deallocate the shmem section without causing a race
+// condition with the other process.
+class ShmemTextureReadLock : public TextureReadLock {
+public:
+ struct ShmReadLockInfo {
+ int32_t readCount;
+ };
+
+ explicit ShmemTextureReadLock(LayersIPCChannel* aAllocator);
+
+ ~ShmemTextureReadLock();
+
+ virtual int32_t ReadLock() override;
+
+ virtual int32_t ReadUnlock() override;
+
+ virtual int32_t GetReadCount() override;
+
+ virtual bool IsValid() const override { return mAllocSuccess; };
+
+ virtual LockType GetType() override { return TYPE_SHMEM; }
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) override;
+
+ mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; }
+
+ explicit ShmemTextureReadLock(const mozilla::layers::ShmemSection& aShmemSection)
+ : mShmemSection(aShmemSection)
+ , mAllocSuccess(true)
+ {
+ MOZ_COUNT_CTOR(ShmemTextureReadLock);
+ }
+
+ ShmReadLockInfo* GetShmReadLockInfoPtr()
+ {
+ return reinterpret_cast<ShmReadLockInfo*>
+ (mShmemSection.shmem().get<char>() + mShmemSection.offset());
+ }
+
+ RefPtr<LayersIPCChannel> mClientAllocator;
+ mozilla::layers::ShmemSection mShmemSection;
+ bool mAllocSuccess;
+};
+
+// static
+already_AddRefed<TextureReadLock>
+TextureReadLock::Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator)
+{
+ switch (aDescriptor.type()) {
+ case ReadLockDescriptor::TShmemSection: {
+ const ShmemSection& section = aDescriptor.get_ShmemSection();
+ MOZ_RELEASE_ASSERT(section.shmem().IsReadable());
+ return MakeAndAddRef<ShmemTextureReadLock>(section);
+ }
+ case ReadLockDescriptor::Tuintptr_t: {
+ if (!aAllocator->IsSameProcess()) {
+ // Trying to use a memory based lock instead of a shmem based one in
+ // the cross-process case is a bad security violation.
+ NS_ERROR("A client process may be trying to peek at the host's address space!");
+ return nullptr;
+ }
+ RefPtr<TextureReadLock> lock = reinterpret_cast<MemoryTextureReadLock*>(
+ aDescriptor.get_uintptr_t()
+ );
+
+ MOZ_ASSERT(lock);
+ if (lock) {
+ // The corresponding AddRef is in MemoryTextureReadLock::Serialize
+ lock.get()->Release();
+ }
+
+ return lock.forget();
+ }
+ case ReadLockDescriptor::Tnull_t: {
+ return nullptr;
+ }
+ default: {
+ // Invalid descriptor.
+ MOZ_DIAGNOSTIC_ASSERT(false);
+ }
+ }
+ return nullptr;
+}
+// static
+already_AddRefed<TextureReadLock>
+TextureReadLock::Create(LayersIPCChannel* aAllocator)
+{
+ if (aAllocator->IsSameProcess()) {
+ // If our compositor is in the same process, we can save some cycles by not
+ // using shared memory.
+ return MakeAndAddRef<MemoryTextureReadLock>();
+ }
+
+ return MakeAndAddRef<ShmemTextureReadLock>(aAllocator);
+}
+
+MemoryTextureReadLock::MemoryTextureReadLock()
+: mReadCount(1)
+{
+ MOZ_COUNT_CTOR(MemoryTextureReadLock);
+}
+
+MemoryTextureReadLock::~MemoryTextureReadLock()
+{
+ // One read count that is added in constructor.
+ MOZ_ASSERT(mReadCount == 1);
+ MOZ_COUNT_DTOR(MemoryTextureReadLock);
+}
+
+bool
+MemoryTextureReadLock::Serialize(ReadLockDescriptor& aOutput)
+{
+ // AddRef here and Release when receiving on the host side to make sure the
+ // reference count doesn't go to zero before the host receives the message.
+ // see TextureReadLock::Deserialize
+ this->AddRef();
+ aOutput = ReadLockDescriptor(uintptr_t(this));
+ return true;
+}
+
+int32_t
+MemoryTextureReadLock::ReadLock()
+{
+ NS_ASSERT_OWNINGTHREAD(MemoryTextureReadLock);
+
+ return PR_ATOMIC_INCREMENT(&mReadCount);
+}
+
+int32_t
+MemoryTextureReadLock::ReadUnlock()
+{
+ int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
+ MOZ_ASSERT(readCount >= 0);
+
+ return readCount;
+}
+
+int32_t
+MemoryTextureReadLock::GetReadCount()
+{
+ NS_ASSERT_OWNINGTHREAD(MemoryTextureReadLock);
+ return mReadCount;
+}
+
+ShmemTextureReadLock::ShmemTextureReadLock(LayersIPCChannel* aAllocator)
+ : mClientAllocator(aAllocator)
+ , mAllocSuccess(false)
+{
+ MOZ_COUNT_CTOR(ShmemTextureReadLock);
+ MOZ_ASSERT(mClientAllocator);
+#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
+ if (mClientAllocator->GetTileLockAllocator()->AllocShmemSection(
+ MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) {
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ info->readCount = 1;
+ mAllocSuccess = true;
+ }
+}
+
+ShmemTextureReadLock::~ShmemTextureReadLock()
+{
+ if (mClientAllocator) {
+ // Release one read count that is added in constructor.
+ // The count is kept for calling GetReadCount() by TextureClientPool.
+ ReadUnlock();
+ }
+ MOZ_COUNT_DTOR(ShmemTextureReadLock);
+}
+
+bool
+ShmemTextureReadLock::Serialize(ReadLockDescriptor& aOutput)
+{
+ aOutput = ReadLockDescriptor(GetShmemSection());
+ return true;
+}
+
+int32_t
+ShmemTextureReadLock::ReadLock() {
+ NS_ASSERT_OWNINGTHREAD(ShmemTextureReadLock);
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ return PR_ATOMIC_INCREMENT(&info->readCount);
+}
+
+int32_t
+ShmemTextureReadLock::ReadUnlock() {
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
+ MOZ_ASSERT(readCount >= 0);
+ if (readCount <= 0) {
+ if (mClientAllocator) {
+ mClientAllocator->GetTileLockAllocator()->DeallocShmemSection(mShmemSection);
+ } else {
+ // we are on the compositor process
+ FixedSizeSmallShmemSectionAllocator::FreeShmemSection(mShmemSection);
+ }
+ }
+ return readCount;
+}
+
+int32_t
+ShmemTextureReadLock::GetReadCount() {
+ NS_ASSERT_OWNINGTHREAD(ShmemTextureReadLock);
+ if (!mAllocSuccess) {
+ return 0;
+ }
+ ShmReadLockInfo* info = GetShmReadLockInfoPtr();
+ return info->readCount;
+}
+
+bool
+UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData)
+{
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->IsLocked());
+ MOZ_ASSERT(aTexture->GetFormat() == gfx::SurfaceFormat::YUV, "This textureClient can only use YCbCr data");
+ MOZ_ASSERT(!aTexture->IsImmutable());
+ MOZ_ASSERT(aTexture->IsValid());
+ MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
+
+ MappedYCbCrTextureData mapped;
+ if (!aTexture->BorrowMappedYCbCrData(mapped)) {
+ NS_WARNING("Failed to extract YCbCr info!");
+ return false;
+ }
+
+ MappedYCbCrTextureData srcData;
+ srcData.y.data = aData.mYChannel;
+ srcData.y.size = aData.mYSize;
+ srcData.y.stride = aData.mYStride;
+ srcData.y.skip = aData.mYSkip;
+ srcData.cb.data = aData.mCbChannel;
+ srcData.cb.size = aData.mCbCrSize;
+ srcData.cb.stride = aData.mCbCrStride;
+ srcData.cb.skip = aData.mCbSkip;
+ srcData.cr.data = aData.mCrChannel;
+ srcData.cr.size = aData.mCbCrSize;
+ srcData.cr.stride = aData.mCbCrStride;
+ srcData.cr.skip = aData.mCrSkip;
+ srcData.metadata = nullptr;
+
+ if (!srcData.CopyInto(mapped)) {
+ NS_WARNING("Failed to copy image data!");
+ return false;
+ }
+
+ if (TextureRequiresLocking(aTexture->GetFlags())) {
+ // We don't have support for proper locking yet, so we'll
+ // have to be immutable instead.
+ aTexture->MarkImmutable();
+ }
+ return true;
+}
+
+already_AddRefed<SyncObject>
+SyncObject::CreateSyncObject(SyncHandle aHandle)
+{
+ if (!aHandle) {
+ return nullptr;
+ }
+
+#ifdef XP_WIN
+ return MakeAndAddRef<SyncObjectD3D11>(aHandle);
+#else
+ MOZ_ASSERT_UNREACHABLE();
+ return nullptr;
+#endif
+}
+
+already_AddRefed<TextureClient>
+TextureClient::CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator)
+{
+ if (!aData) {
+ return nullptr;
+ }
+ return MakeAndAddRef<TextureClient>(aData, aFlags, aAllocator);
+}
+
+bool
+MappedYCbCrChannelData::CopyInto(MappedYCbCrChannelData& aDst)
+{
+ if (!data || !aDst.data || size != aDst.size) {
+ return false;
+ }
+
+ if (stride == aDst.stride) {
+ // fast path!
+ // We assume that the padding in the destination is there for alignment
+ // purposes and doesn't contain useful data.
+ memcpy(aDst.data, data, stride * size.height);
+ return true;
+ }
+
+ for (int32_t i = 0; i < size.height; ++i) {
+ if (aDst.skip == 0 && skip == 0) {
+ // fast-ish path
+ memcpy(aDst.data + i * aDst.stride,
+ data + i * stride,
+ size.width);
+ } else {
+ // slow path
+ uint8_t* src = data + i * stride;
+ uint8_t* dst = aDst.data + i * aDst.stride;
+ for (int32_t j = 0; j < size.width; ++j) {
+ *dst = *src;
+ src += 1 + skip;
+ dst += 1 + aDst.skip;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h
new file mode 100644
index 000000000..d28f37cf5
--- /dev/null
+++ b/gfx/layers/client/TextureClient.h
@@ -0,0 +1,826 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_H
+#define MOZILLA_GFX_TEXTURECLIENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include "GLTextureImage.h" // for TextureImage
+#include "ImageTypes.h" // for StereoMode
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/DebugOnly.h"
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
+#include "mozilla/gfx/2D.h" // for DrawTarget
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+#include "mozilla/mozalloc.h" // for operator delete
+#include "mozilla/gfx/CriticalSection.h"
+#include "nsCOMPtr.h" // for already_AddRefed
+#include "nsISupportsImpl.h" // for TextureImage::AddRef, etc
+#include "GfxTexturesReporter.h"
+#include "pratom.h"
+#include "nsThreadUtils.h"
+
+class gfxImageSurface;
+
+namespace mozilla {
+
+// When defined, we track which pool the tile came from and test for
+// any inconsistencies. This can be defined in release build as well.
+#ifdef DEBUG
+#define GFX_DEBUG_TRACK_CLIENTS_IN_POOL 1
+#endif
+
+namespace layers {
+
+class AsyncTransactionWaiter;
+class BufferTextureData;
+class CompositableForwarder;
+class KnowsCompositor;
+class LayersIPCChannel;
+class CompositableClient;
+struct PlanarYCbCrData;
+class Image;
+class PTextureChild;
+class TextureChild;
+class TextureData;
+class GPUVideoTextureData;
+struct RawTextureBuffer;
+class RawYCbCrTextureBuffer;
+class TextureClient;
+class ITextureClientRecycleAllocator;
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+class TextureClientPool;
+#endif
+class TextureForwarder;
+class KeepAlive;
+
+/**
+ * TextureClient is the abstraction that allows us to share data between the
+ * content and the compositor side.
+ */
+
+enum TextureAllocationFlags {
+ ALLOC_DEFAULT = 0,
+ ALLOC_CLEAR_BUFFER = 1 << 1, // Clear the buffer to whatever is best for the draw target
+ ALLOC_CLEAR_BUFFER_WHITE = 1 << 2, // explicit all white
+ ALLOC_CLEAR_BUFFER_BLACK = 1 << 3, // explicit all black
+ ALLOC_DISALLOW_BUFFERTEXTURECLIENT = 1 << 4,
+
+ // Allocate the texture for out-of-band content updates. This is mostly for
+ // TextureClientD3D11, which may otherwise choose D3D10 or non-KeyedMutex
+ // surfaces when used on the main thread.
+ ALLOC_FOR_OUT_OF_BAND_CONTENT = 1 << 5,
+
+ // Disable any cross-device synchronization. This is also for TextureClientD3D11,
+ // and creates a texture without KeyedMutex.
+ ALLOC_MANUAL_SYNCHRONIZATION = 1 << 6,
+
+ // The texture is going to be updated using UpdateFromSurface and needs to support
+ // that call.
+ ALLOC_UPDATE_FROM_SURFACE = 1 << 7,
+};
+
+#ifdef XP_WIN
+typedef void* SyncHandle;
+#else
+typedef uintptr_t SyncHandle;
+#endif // XP_WIN
+
+class SyncObject : public RefCounted<SyncObject>
+{
+public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SyncObject)
+ virtual ~SyncObject() { }
+
+ static already_AddRefed<SyncObject> CreateSyncObject(SyncHandle aHandle);
+
+ enum class SyncType {
+ D3D11,
+ };
+
+ virtual SyncType GetSyncType() = 0;
+ virtual void FinalizeFrame() = 0;
+ virtual bool IsSyncObjectValid() = 0;
+
+protected:
+ SyncObject() { }
+};
+
+/**
+ * This class may be used to asynchronously receive an update when the content
+ * drawn to this texture client is available for reading in CPU memory. This
+ * can only be used on texture clients that support draw target creation.
+ */
+class TextureReadbackSink
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadbackSink)
+public:
+ /**
+ * Callback function to implement in order to receive a DataSourceSurface
+ * containing the data read back from the texture client. This will always
+ * be called on the main thread, and this may not hold on to the
+ * DataSourceSurface beyond the execution of this function.
+ */
+ virtual void ProcessReadback(gfx::DataSourceSurface *aSourceSurface) = 0;
+
+protected:
+ virtual ~TextureReadbackSink() {}
+};
+
+enum class BackendSelector
+{
+ Content,
+ Canvas
+};
+
+/// Temporary object providing direct access to a Texture's memory.
+///
+/// see TextureClient::CanExposeMappedData() and TextureClient::BorrowMappedData().
+struct MappedTextureData
+{
+ uint8_t* data;
+ gfx::IntSize size;
+ int32_t stride;
+ gfx::SurfaceFormat format;
+};
+
+struct MappedYCbCrChannelData
+{
+ uint8_t* data;
+ gfx::IntSize size;
+ int32_t stride;
+ int32_t skip;
+
+ bool CopyInto(MappedYCbCrChannelData& aDst);
+};
+
+struct MappedYCbCrTextureData {
+ MappedYCbCrChannelData y;
+ MappedYCbCrChannelData cb;
+ MappedYCbCrChannelData cr;
+ // Sad but because of how SharedPlanarYCbCrData is used we have to expose this for now.
+ uint8_t* metadata;
+ StereoMode stereoMode;
+
+ bool CopyInto(MappedYCbCrTextureData& aDst)
+ {
+ return y.CopyInto(aDst.y)
+ && cb.CopyInto(aDst.cb)
+ && cr.CopyInto(aDst.cr);
+ }
+};
+
+class ReadLockDescriptor;
+
+// A class to help implement copy-on-write semantics for shared textures.
+//
+// A TextureClient/Host pair can opt into using a ReadLock by calling
+// TextureClient::EnableReadLock. This will equip the TextureClient with a
+// ReadLock object that will be automatically ReadLock()'ed by the texture itself
+// when it is written into (see TextureClient::Unlock).
+// A TextureReadLock's counter starts at 1 and is expected to be equal to 1 when the
+// lock is destroyed. See ShmemTextureReadLock for explanations about why we use
+// 1 instead of 0 as the initial state.
+// TextureReadLock is mostly internally managed by the TextureClient/Host pair,
+// and the compositable only has to forward it during updates. If an update message
+// contains a null_t lock, it means that the texture was not written into on the
+// content side, and there is no synchronization required on the compositor side
+// (or it means that the texture pair did not opt into using ReadLocks).
+// On the compositor side, the TextureHost can receive a ReadLock during a
+// transaction, and will both ReadUnlock() it and drop it as soon as the shared
+// data is available again for writing (the texture upload is done, or the compositor
+// not reading the texture anymore). The lock is dropped to make sure it is
+// ReadUnlock()'ed only once.
+class TextureReadLock {
+protected:
+ virtual ~TextureReadLock() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadLock)
+
+ virtual int32_t ReadLock() = 0;
+ virtual int32_t ReadUnlock() = 0;
+ virtual int32_t GetReadCount() = 0;
+ virtual bool IsValid() const = 0;
+
+ static already_AddRefed<TextureReadLock>
+ Create(LayersIPCChannel* aAllocator);
+
+ static already_AddRefed<TextureReadLock>
+ Deserialize(const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator);
+
+ virtual bool Serialize(ReadLockDescriptor& aOutput) = 0;
+
+ enum LockType {
+ TYPE_MEMORY,
+ TYPE_SHMEM
+ };
+ virtual LockType GetType() = 0;
+
+protected:
+ NS_DECL_OWNINGTHREAD
+};
+
+#ifdef XP_WIN
+class D3D11TextureData;
+#endif
+
+class TextureData {
+public:
+ struct Info {
+ gfx::IntSize size;
+ gfx::SurfaceFormat format;
+ bool hasIntermediateBuffer;
+ bool hasSynchronization;
+ bool supportsMoz2D;
+ bool canExposeMappedData;
+
+ Info()
+ : format(gfx::SurfaceFormat::UNKNOWN)
+ , hasIntermediateBuffer(false)
+ , hasSynchronization(false)
+ , supportsMoz2D(false)
+ , canExposeMappedData(false)
+ {}
+ };
+
+ TextureData() { MOZ_COUNT_CTOR(TextureData); }
+
+ virtual ~TextureData() { MOZ_COUNT_DTOR(TextureData); }
+
+ virtual void FillInfo(TextureData::Info& aInfo) const = 0;
+
+ virtual bool Lock(OpenMode aMode) = 0;
+
+ virtual void Unlock() = 0;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
+
+ virtual bool BorrowMappedData(MappedTextureData&) { return false; }
+
+ virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
+
+ virtual void Deallocate(LayersIPCChannel* aAllocator) = 0;
+
+ /// Depending on the texture's flags either Deallocate or Forget is called.
+ virtual void Forget(LayersIPCChannel* aAllocator) {}
+
+ virtual bool Serialize(SurfaceDescriptor& aDescriptor) = 0;
+
+ virtual TextureData*
+ CreateSimilar(LayersIPCChannel* aAllocator,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const { return nullptr; }
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) { return false; };
+
+ virtual bool ReadBack(TextureReadbackSink* aReadbackSink) { return false; }
+
+ virtual void SyncWithObject(SyncObject* aFence) {};
+
+ virtual TextureFlags GetTextureFlags() const { return TextureFlags::NO_FLAGS; }
+
+#ifdef XP_WIN
+ virtual D3D11TextureData* AsD3D11TextureData() {
+ return nullptr;
+ }
+#endif
+
+ virtual BufferTextureData* AsBufferTextureData() { return nullptr; }
+
+ virtual GPUVideoTextureData* AsGPUVideoTextureData() { return nullptr; }
+};
+
+/**
+ * TextureClient is a thin abstraction over texture data that need to be shared
+ * between the content process and the compositor process. It is the
+ * content-side half of a TextureClient/TextureHost pair. A corresponding
+ * TextureHost lives on the compositor-side.
+ *
+ * TextureClient's primary purpose is to present texture data in a way that is
+ * understood by the IPC system. There are two ways to use it:
+ * - Use it to serialize image data that is not IPC-friendly (most likely
+ * involving a copy into shared memory)
+ * - preallocate it and paint directly into it, which avoids copy but requires
+ * the painting code to be aware of TextureClient (or at least the underlying
+ * shared memory).
+ *
+ * There is always one and only one TextureClient per TextureHost, and the
+ * TextureClient/Host pair only owns one buffer of image data through its
+ * lifetime. This means that the lifetime of the underlying shared data
+ * matches the lifetime of the TextureClient/Host pair. It also means
+ * TextureClient/Host do not implement double buffering, which is the
+ * responsibility of the compositable (which would use two Texture pairs).
+ * In order to send several different buffers to the compositor side, use
+ * several TextureClients.
+ */
+class TextureClient
+ : public AtomicRefCountedWithFinalize<TextureClient>
+{
+public:
+ explicit TextureClient(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ virtual ~TextureClient();
+
+ static already_AddRefed<TextureClient>
+ CreateWithData(TextureData* aData, TextureFlags aFlags, LayersIPCChannel* aAllocator);
+
+ // Creates and allocates a TextureClient usable with Moz2D.
+ static already_AddRefed<TextureClient>
+ CreateForDrawing(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ static already_AddRefed<TextureClient>
+ CreateFromSurface(KnowsCompositor* aAllocator,
+ gfx::SourceSurface* aSurface,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags);
+
+ // Creates and allocates a TextureClient supporting the YCbCr format.
+ static already_AddRefed<TextureClient>
+ CreateForYCbCr(KnowsCompositor* aAllocator,
+ gfx::IntSize aYSize,
+ gfx::IntSize aCbCrSize,
+ StereoMode aStereoMode,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // Creates and allocates a TextureClient (can be accessed through raw
+ // pointers).
+ static already_AddRefed<TextureClient>
+ CreateForRawBufferAccess(KnowsCompositor* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ // Creates and allocates a TextureClient (can beaccessed through raw
+ // pointers) with a certain buffer size. It's unfortunate that we need this.
+ // providing format and sizes could let us do more optimization.
+ static already_AddRefed<TextureClient>
+ CreateForYCbCrWithBufferSize(KnowsCompositor* aAllocator,
+ size_t aSize,
+ YUVColorSpace aYUVColorSpace,
+ TextureFlags aTextureFlags);
+
+ // Creates and allocates a TextureClient of the same type.
+ already_AddRefed<TextureClient>
+ CreateSimilar(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
+ TextureFlags aFlags = TextureFlags::DEFAULT,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const;
+
+ /**
+ * Locks the shared data, allowing the caller to get access to it.
+ *
+ * Please always lock/unlock when accessing the shared data.
+ * If Lock() returns false, you should not attempt to access the shared data.
+ */
+ bool Lock(OpenMode aMode);
+
+ void Unlock();
+
+ bool IsLocked() const { return mIsLocked; }
+
+ gfx::IntSize GetSize() const { return mInfo.size; }
+
+ gfx::SurfaceFormat GetFormat() const { return mInfo.format; }
+
+ /**
+ * Returns true if this texture has a synchronization mechanism (mutex, fence, etc.).
+ * Textures that do not implement synchronization should be immutable or should
+ * use immediate uploads (see TextureFlags in CompositorTypes.h)
+ * Even if a texture does not implement synchronization, Lock and Unlock need
+ * to be used appropriately since the latter are also there to map/numap data.
+ */
+ bool HasSynchronization() const { return mInfo.hasSynchronization; }
+
+ /**
+ * Indicates whether the TextureClient implementation is backed by an
+ * in-memory buffer. The consequence of this is that locking the
+ * TextureClient does not contend with locking the texture on the host side.
+ */
+ bool HasIntermediateBuffer() const { return mInfo.hasIntermediateBuffer; }
+
+ bool CanExposeDrawTarget() const { return mInfo.supportsMoz2D; }
+
+ bool CanExposeMappedData() const { return mInfo.canExposeMappedData; }
+
+ /**
+ * Returns a DrawTarget to draw into the TextureClient.
+ * This function should never be called when not on the main thread!
+ *
+ * This must never be called on a TextureClient that is not sucessfully locked.
+ * When called several times within one Lock/Unlock pair, this method should
+ * return the same DrawTarget.
+ * The DrawTarget is automatically flushed by the TextureClient when the latter
+ * is unlocked, and the DrawTarget that will be returned within the next
+ * lock/unlock pair may or may not be the same object.
+ * Do not keep references to the DrawTarget outside of the lock/unlock pair.
+ *
+ * This is typically used as follows:
+ *
+ * if (!texture->Lock(OpenMode::OPEN_READ_WRITE)) {
+ * return false;
+ * }
+ * {
+ * // Restrict this code's scope to ensure all references to dt are gone
+ * // when Unlock is called.
+ * DrawTarget* dt = texture->BorrowDrawTarget();
+ * // use the draw target ...
+ * }
+ * texture->Unlock();
+ *
+ */
+ gfx::DrawTarget* BorrowDrawTarget();
+
+ /**
+ * Similar to BorrowDrawTarget but provides direct access to the texture's bits
+ * instead of a DrawTarget.
+ */
+ bool BorrowMappedData(MappedTextureData&);
+ bool BorrowMappedYCbCrData(MappedYCbCrTextureData&);
+
+ /**
+ * This function can be used to update the contents of the TextureClient
+ * off the main thread.
+ */
+ void UpdateFromSurface(gfx::SourceSurface* aSurface);
+
+ /**
+ * This method is strictly for debugging. It causes locking and
+ * needless copies.
+ */
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ /**
+ * Copies a rectangle from this texture client to a position in aTarget.
+ * It is assumed that the necessary locks are in place; so this should at
+ * least have a read lock and aTarget should at least have a write lock.
+ */
+ bool CopyToTextureClient(TextureClient* aTarget,
+ const gfx::IntRect* aRect,
+ const gfx::IntPoint* aPoint);
+
+ /**
+ * Allocate and deallocate a TextureChild actor.
+ *
+ * TextureChild is an implementation detail of TextureClient that is not
+ * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
+ * are for use with the managing IPDL protocols only (so that they can
+ * implement AllocPextureChild and DeallocPTextureChild).
+ */
+ static PTextureChild* CreateIPDLActor();
+ static bool DestroyIPDLActor(PTextureChild* actor);
+
+ /**
+ * Get the TextureClient corresponding to the actor passed in parameter.
+ */
+ static already_AddRefed<TextureClient> AsTextureClient(PTextureChild* actor);
+
+ /**
+ * TextureFlags contain important information about various aspects
+ * of the texture, like how its liferime is managed, and how it
+ * should be displayed.
+ * See TextureFlags in CompositorTypes.h.
+ */
+ TextureFlags GetFlags() const { return mFlags; }
+
+ bool HasFlags(TextureFlags aFlags) const
+ {
+ return (mFlags & aFlags) == aFlags;
+ }
+
+ void AddFlags(TextureFlags aFlags);
+
+ void RemoveFlags(TextureFlags aFlags);
+
+ // Must not be called when TextureClient is in use by CompositableClient.
+ void RecycleTexture(TextureFlags aFlags);
+
+ /**
+ * After being shared with the compositor side, an immutable texture is never
+ * modified, it can only be read. It is safe to not Lock/Unlock immutable
+ * textures.
+ */
+ bool IsImmutable() const { return !!(mFlags & TextureFlags::IMMUTABLE); }
+
+ void MarkImmutable() { AddFlags(TextureFlags::IMMUTABLE); }
+
+ bool IsSharedWithCompositor() const;
+
+ /**
+ * If this method returns false users of TextureClient are not allowed
+ * to access the shared data.
+ */
+ bool IsValid() const { return !!mData; }
+
+ /**
+ * Called when TextureClient is added to CompositableClient.
+ */
+ void SetAddedToCompositableClient();
+
+ /**
+ * If this method retuns false, TextureClient is already added to CompositableClient,
+ * since its creation or recycling.
+ */
+ bool IsAddedToCompositableClient() const { return mAddedToCompositableClient; }
+
+ /**
+ * Create and init the TextureChild/Parent IPDL actor pair
+ * with a CompositableForwarder.
+ *
+ * Should be called only once per TextureClient.
+ * The TextureClient must not be locked when calling this method.
+ */
+ bool InitIPDLActor(CompositableForwarder* aForwarder);
+
+ /**
+ * Create and init the TextureChild/Parent IPDL actor pair
+ * with a TextureForwarder.
+ *
+ * Should be called only once per TextureClient.
+ * The TextureClient must not be locked when calling this method.
+ */
+ bool InitIPDLActor(KnowsCompositor* aForwarder);
+
+ /**
+ * Return a pointer to the IPDLActor.
+ *
+ * This is to be used with IPDL messages only. Do not store the returned
+ * pointer.
+ */
+ PTextureChild* GetIPDLActor();
+
+ /**
+ * Triggers the destruction of the shared data and the corresponding TextureHost.
+ *
+ * If the texture flags contain TextureFlags::DEALLOCATE_CLIENT, the destruction
+ * will be synchronously coordinated with the compositor side, otherwise it
+ * will be done asynchronously.
+ * If sync is true, the destruction will be synchronous regardless of the
+ * texture's flags (bad for performance, use with care).
+ */
+ void Destroy(bool sync = false);
+
+ /**
+ * Track how much of this texture is wasted.
+ * For example we might allocate a 256x256 tile but only use 10x10.
+ */
+ void SetWaste(int aWasteArea) {
+ mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
+ }
+
+ /**
+ * This sets the readback sink that this texture is to use. This will
+ * receive the data for this texture as soon as it becomes available after
+ * texture unlock.
+ */
+ virtual void SetReadbackSink(TextureReadbackSink* aReadbackSink) {
+ mReadbackSink = aReadbackSink;
+ }
+
+ void SyncWithObject(SyncObject* aFence) { mData->SyncWithObject(aFence); }
+
+ LayersIPCChannel* GetAllocator() { return mAllocator; }
+
+ ITextureClientRecycleAllocator* GetRecycleAllocator() { return mRecycleAllocator; }
+ void SetRecycleAllocator(ITextureClientRecycleAllocator* aAllocator);
+
+ /// If you add new code that uses this method, you are probably doing something wrong.
+ TextureData* GetInternalData() { return mData; }
+ const TextureData* GetInternalData() const { return mData; }
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ void CancelWaitForRecycle();
+
+ /**
+ * Set last transaction id of CompositableForwarder.
+ *
+ * Called when TextureClient has TextureFlags::RECYCLE flag.
+ * When CompositableForwarder forwards the TextureClient with
+ * TextureFlags::RECYCLE, it holds TextureClient's ref until host side
+ * releases it. The host side sends TextureClient release message.
+ * The id is used to check if the message is for the last TextureClient
+ * forwarding.
+ */
+ void SetLastFwdTransactionId(uint64_t aTransactionId)
+ {
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+ }
+
+ uint64_t GetLastFwdTransactionId() { return mFwdTransactionId; }
+
+ void EnableReadLock();
+
+ TextureReadLock* GetReadLock() { return mReadLock; }
+
+ bool IsReadLocked() const;
+
+ void SerializeReadLock(ReadLockDescriptor& aDescriptor);
+
+private:
+ static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
+
+ // Internal helpers for creating texture clients using the actual forwarder instead
+ // of KnowsCompositor. TextureClientPool uses these to let it cache texture clients
+ // per-process instead of per ShadowLayerForwarder, but everyone else should
+ // use the public functions instead.
+ friend class TextureClientPool;
+ static already_AddRefed<TextureClient>
+ CreateForDrawing(TextureForwarder* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT);
+
+ static already_AddRefed<TextureClient>
+ CreateForRawBufferAccess(LayersIPCChannel* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ gfx::BackendType aMoz2dBackend,
+ LayersBackend aLayersBackend,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ /**
+ * Called once, during the destruction of the Texture, on the thread in which
+ * texture's reference count reaches 0 (could be any thread).
+ *
+ * Here goes the shut-down code that uses virtual methods.
+ * Must only be called by Release().
+ */
+ void Finalize() {}
+
+ friend class AtomicRefCountedWithFinalize<TextureClient>;
+protected:
+ /**
+ * Should only be called *once* per texture, in TextureClient::InitIPDLActor.
+ * Some texture implementations rely on the fact that the descriptor will be
+ * deserialized.
+ * Calling ToSurfaceDescriptor again after it has already returned true,
+ * or never constructing a TextureHost with aDescriptor may result in a memory
+ * leak (see TextureClientD3D9 for example).
+ */
+ bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor);
+
+ void LockActor() const;
+ void UnlockActor() const;
+
+ TextureData::Info mInfo;
+
+ RefPtr<LayersIPCChannel> mAllocator;
+ RefPtr<TextureChild> mActor;
+ RefPtr<ITextureClientRecycleAllocator> mRecycleAllocator;
+ RefPtr<TextureReadLock> mReadLock;
+
+ TextureData* mData;
+ RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
+
+ TextureFlags mFlags;
+
+ gl::GfxTextureWasteTracker mWasteTracker;
+
+ OpenMode mOpenMode;
+#ifdef DEBUG
+ uint32_t mExpectedDtRefs;
+#endif
+ bool mIsLocked;
+ // This member tracks that the texture was written into until the update
+ // is sent to the compositor. We need this remember to lock mReadLock on
+ // behalf of the compositor just before sending the notification.
+ bool mUpdated;
+
+ // Used when TextureClient is recycled with TextureFlags::RECYCLE flag.
+ bool mAddedToCompositableClient;
+
+ bool mWorkaroundAnnoyingSharedSurfaceLifetimeIssues;
+ bool mWorkaroundAnnoyingSharedSurfaceOwnershipIssues;
+
+ RefPtr<TextureReadbackSink> mReadbackSink;
+
+ uint64_t mFwdTransactionId;
+
+ // Serial id of TextureClient. It is unique in current process.
+ const uint64_t mSerial;
+ // Used to assign serial ids of TextureClient.
+ static mozilla::Atomic<uint64_t> sSerialCounter;
+
+ friend class TextureChild;
+ friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
+ friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
+
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+public:
+ // Pointer to the pool this tile came from.
+ TextureClientPool* mPoolTracker;
+#endif
+};
+
+/**
+ * Task that releases TextureClient pointer on a specified thread.
+ */
+class TextureClientReleaseTask : public Runnable
+{
+public:
+ explicit TextureClientReleaseTask(TextureClient* aClient)
+ : mTextureClient(aClient) {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ mTextureClient = nullptr;
+ return NS_OK;
+ }
+
+private:
+ RefPtr<TextureClient> mTextureClient;
+};
+
+// Automatically lock and unlock a texture. Since texture locking is fallible,
+// Succeeded() must be checked on the guard object before proceeding.
+class MOZ_RAII TextureClientAutoLock
+{
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+public:
+ TextureClientAutoLock(TextureClient* aTexture, OpenMode aMode
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mTexture(aTexture),
+ mSucceeded(false)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+ mSucceeded = mTexture->Lock(aMode);
+#ifdef DEBUG
+ mChecked = false;
+#endif
+ }
+ ~TextureClientAutoLock() {
+ MOZ_ASSERT(mChecked);
+ if (mSucceeded) {
+ mTexture->Unlock();
+ }
+ }
+
+ bool Succeeded() {
+#ifdef DEBUG
+ mChecked = true;
+#endif
+ return mSucceeded;
+ }
+
+private:
+ TextureClient* mTexture;
+#ifdef DEBUG
+ bool mChecked;
+#endif
+ bool mSucceeded;
+};
+
+class KeepAlive
+{
+public:
+ virtual ~KeepAlive() {}
+};
+
+template<typename T>
+class TKeepAlive : public KeepAlive
+{
+public:
+ explicit TKeepAlive(T* aData) : mData(aData) {}
+protected:
+ RefPtr<T> mData;
+};
+
+/// Convenience function to set the content of ycbcr texture.
+bool UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData);
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/client/TextureClientPool.cpp b/gfx/layers/client/TextureClientPool.cpp
new file mode 100644
index 000000000..c556a791e
--- /dev/null
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -0,0 +1,339 @@
+/* -*- 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 "TextureClientPool.h"
+#include "CompositableClient.h"
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/TiledContentClient.h"
+
+#include "gfxPrefs.h"
+
+#include "nsComponentManagerUtils.h"
+
+#define TCP_LOG(...)
+//#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
+
+namespace mozilla {
+namespace layers {
+
+// We want to shrink to our maximum size of N unused tiles
+// after a timeout to allow for short-term budget requirements
+static void
+ShrinkCallback(nsITimer *aTimer, void *aClosure)
+{
+ static_cast<TextureClientPool*>(aClosure)->ShrinkToMaximumSize();
+}
+
+// After a certain amount of inactivity, let's clear the pool so that
+// we don't hold onto tiles needlessly. In general, allocations are
+// cheap enough that re-allocating isn't an issue unless we're allocating
+// at an inopportune time (e.g. mid-animation).
+static void
+ClearCallback(nsITimer *aTimer, void *aClosure)
+{
+ static_cast<TextureClientPool*>(aClosure)->Clear();
+}
+
+TextureClientPool::TextureClientPool(LayersBackend aLayersBackend,
+ int32_t aMaxTextureSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ uint32_t aShrinkTimeoutMsec,
+ uint32_t aClearTimeoutMsec,
+ uint32_t aInitialPoolSize,
+ uint32_t aPoolUnusedSize,
+ TextureForwarder* aAllocator)
+ : mBackend(aLayersBackend)
+ , mMaxTextureSize(aMaxTextureSize)
+ , mFormat(aFormat)
+ , mSize(aSize)
+ , mFlags(aFlags)
+ , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
+ , mClearTimeoutMsec(aClearTimeoutMsec)
+ , mInitialPoolSize(aInitialPoolSize)
+ , mPoolUnusedSize(aPoolUnusedSize)
+ , mOutstandingClients(0)
+ , mSurfaceAllocator(aAllocator)
+ , mDestroyed(false)
+{
+ TCP_LOG("TexturePool %p created with maximum unused texture clients %u\n",
+ this, mInitialPoolSize);
+ mShrinkTimer = do_CreateInstance("@mozilla.org/timer;1");
+ mClearTimer = do_CreateInstance("@mozilla.org/timer;1");
+ if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+ gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
+ }
+}
+
+TextureClientPool::~TextureClientPool()
+{
+ mShrinkTimer->Cancel();
+ mClearTimer->Cancel();
+}
+
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+static bool TestClientPool(const char* what,
+ TextureClient* aClient,
+ TextureClientPool* aPool)
+{
+ if (!aClient || !aPool) {
+ return false;
+ }
+
+ TextureClientPool* actual = aClient->mPoolTracker;
+ bool ok = (actual == aPool);
+ if (ok) {
+ ok = (aClient->GetFormat() == aPool->GetFormat());
+ }
+
+ if (!ok) {
+ if (actual) {
+ gfxCriticalError() << "Pool error(" << what << "): "
+ << aPool << "-" << aPool->GetFormat() << ", "
+ << actual << "-" << actual->GetFormat() << ", "
+ << aClient->GetFormat();
+ MOZ_CRASH("GFX: Crashing with actual");
+ } else {
+ gfxCriticalError() << "Pool error(" << what << "): "
+ << aPool << "-" << aPool->GetFormat() << ", nullptr, "
+ << aClient->GetFormat();
+ MOZ_CRASH("GFX: Crashing without actual");
+ }
+ }
+ return ok;
+}
+#endif
+
+already_AddRefed<TextureClient>
+TextureClientPool::GetTextureClient()
+{
+ // Try to fetch a client from the pool
+ RefPtr<TextureClient> textureClient;
+
+ // We initially allocate mInitialPoolSize for our pool. If we run
+ // out of TextureClients, we allocate additional TextureClients to try and keep around
+ // mPoolUnusedSize
+ if (!mTextureClients.size()) {
+ AllocateTextureClient();
+ }
+
+ if (!mTextureClients.size()) {
+ // All our allocations failed, return nullptr
+ return nullptr;
+ }
+
+ mOutstandingClients++;
+ textureClient = mTextureClients.top();
+ mTextureClients.pop();
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ if (textureClient) {
+ textureClient->mPoolTracker = this;
+ }
+ DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
+ this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
+
+ return textureClient.forget();
+}
+
+void
+TextureClientPool::AllocateTextureClient()
+{
+ TCP_LOG("TexturePool %p allocating TextureClient, outstanding %u\n",
+ this, mOutstandingClients);
+
+ RefPtr<TextureClient> newClient;
+ if (gfxPrefs::ForceShmemTiles()) {
+ // gfx::BackendType::NONE means use the content backend
+ newClient =
+ TextureClient::CreateForRawBufferAccess(mSurfaceAllocator,
+ mFormat, mSize,
+ gfx::BackendType::NONE,
+ mBackend,
+ mFlags, ALLOC_DEFAULT);
+ } else {
+ newClient =
+ TextureClient::CreateForDrawing(mSurfaceAllocator,
+ mFormat, mSize,
+ mBackend,
+ mMaxTextureSize,
+ BackendSelector::Content,
+ mFlags);
+ }
+
+ if (newClient) {
+ mTextureClients.push(newClient);
+ }
+}
+
+void
+TextureClientPool::ResetTimers()
+{
+ // Shrink down if we're beyond our maximum size
+ if (mShrinkTimeoutMsec &&
+ mTextureClients.size() + mTextureClientsDeferred.size() > mPoolUnusedSize) {
+ TCP_LOG("TexturePool %p scheduling a shrink-to-max-size\n", this);
+ mShrinkTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+
+ // Clear pool after a period of inactivity to reduce memory consumption
+ if (mClearTimeoutMsec) {
+ TCP_LOG("TexturePool %p scheduling a clear\n", this);
+ mClearTimer->InitWithFuncCallback(ClearCallback, this, mClearTimeoutMsec,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+void
+TextureClientPool::ReturnTextureClient(TextureClient *aClient)
+{
+ if (!aClient || mDestroyed) {
+ return;
+ }
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ DebugOnly<bool> ok = TestClientPool("return", aClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ // Add the client to the pool:
+ MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+ mOutstandingClients--;
+ mTextureClients.push(aClient);
+ TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
+ this, aClient, mTextureClients.size(), mOutstandingClients);
+
+ ResetTimers();
+}
+
+void
+TextureClientPool::ReturnTextureClientDeferred(TextureClient* aClient)
+{
+ if (!aClient || mDestroyed) {
+ return;
+ }
+ MOZ_ASSERT(aClient->GetReadLock());
+#ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
+ DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
+ MOZ_ASSERT(ok);
+#endif
+ mTextureClientsDeferred.push_back(aClient);
+ TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
+ this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
+
+ ResetTimers();
+}
+
+void
+TextureClientPool::ShrinkToMaximumSize()
+{
+ // We're over our desired maximum size, immediately shrink down to the
+ // maximum.
+ //
+ // We cull from the deferred TextureClients first, as we can't reuse those
+ // until they get returned.
+ uint32_t totalUnusedTextureClients = mTextureClients.size() + mTextureClientsDeferred.size();
+
+ // If we have > mInitialPoolSize outstanding, then we want to keep around
+ // mPoolUnusedSize at a maximum. If we have fewer than mInitialPoolSize
+ // outstanding, then keep around the entire initial pool size.
+ uint32_t targetUnusedClients;
+ if (mOutstandingClients > mInitialPoolSize) {
+ targetUnusedClients = mPoolUnusedSize;
+ } else {
+ targetUnusedClients = mInitialPoolSize;
+ }
+
+ TCP_LOG("TexturePool %p shrinking to maximum unused size %u; current pool size %u; total outstanding %u\n",
+ this, targetUnusedClients, totalUnusedTextureClients, mOutstandingClients);
+
+ while (totalUnusedTextureClients > targetUnusedClients) {
+ if (mTextureClientsDeferred.size()) {
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
+ this, mTextureClientsDeferred.front().get(),
+ mTextureClientsDeferred.size() - 1);
+ mTextureClientsDeferred.pop_front();
+ } else {
+ TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
+ this, mTextureClients.top().get(), mTextureClients.size() - 1);
+ mTextureClients.pop();
+ }
+ totalUnusedTextureClients--;
+ }
+}
+
+void
+TextureClientPool::ReturnDeferredClients()
+{
+ if (mTextureClientsDeferred.empty()) {
+ return;
+ }
+
+ TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
+ this, mTextureClientsDeferred.size());
+
+ ReturnUnlockedClients();
+ ShrinkToMaximumSize();
+}
+
+void
+TextureClientPool::ReturnUnlockedClients()
+{
+ for (auto it = mTextureClientsDeferred.begin(); it != mTextureClientsDeferred.end();) {
+ MOZ_ASSERT((*it)->GetReadLock()->GetReadCount() >= 1);
+ // Last count is held by the lock itself.
+ if (!(*it)->IsReadLocked()) {
+ mTextureClients.push(*it);
+ it = mTextureClientsDeferred.erase(it);
+
+ MOZ_ASSERT(mOutstandingClients > 0);
+ mOutstandingClients--;
+ } else {
+ it++;
+ }
+ }
+}
+
+void
+TextureClientPool::ReportClientLost()
+{
+ MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
+ this, mOutstandingClients);
+}
+
+void
+TextureClientPool::Clear()
+{
+ TCP_LOG("TexturePool %p getting cleared\n", this);
+ while (!mTextureClients.empty()) {
+ TCP_LOG("TexturePool %p releasing client %p\n",
+ this, mTextureClients.top().get());
+ mTextureClients.pop();
+ }
+ while (!mTextureClientsDeferred.empty()) {
+ MOZ_ASSERT(mOutstandingClients > 0);
+ mOutstandingClients--;
+ TCP_LOG("TexturePool %p releasing deferred client %p\n",
+ this, mTextureClientsDeferred.front().get());
+ mTextureClientsDeferred.pop_front();
+ }
+}
+
+void TextureClientPool::Destroy()
+{
+ Clear();
+ mDestroyed = true;
+ mInitialPoolSize = 0;
+ mPoolUnusedSize = 0;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientPool.h b/gfx/layers/client/TextureClientPool.h
new file mode 100644
index 000000000..642c14bf5
--- /dev/null
+++ b/gfx/layers/client/TextureClientPool.h
@@ -0,0 +1,180 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENTPOOL_H
+#define MOZILLA_GFX_TEXTURECLIENTPOOL_H
+
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/RefPtr.h"
+#include "TextureClient.h"
+#include "nsITimer.h"
+#include <stack>
+#include <list>
+
+namespace mozilla {
+namespace layers {
+
+class ISurfaceAllocator;
+class TextureForwarder;
+class TextureReadLock;
+
+class TextureClientAllocator
+{
+protected:
+ virtual ~TextureClientAllocator() {}
+public:
+ NS_INLINE_DECL_REFCOUNTING(TextureClientAllocator)
+
+ virtual already_AddRefed<TextureClient> GetTextureClient() = 0;
+
+ /**
+ * Return a TextureClient that is not yet ready to be reused, but will be
+ * imminently.
+ */
+ virtual void ReturnTextureClientDeferred(TextureClient *aClient) = 0;
+
+ virtual void ReportClientLost() = 0;
+};
+
+class TextureClientPool final : public TextureClientAllocator
+{
+ ~TextureClientPool();
+
+public:
+ TextureClientPool(LayersBackend aBackend,
+ int32_t aMaxTextureSize,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ TextureFlags aFlags,
+ uint32_t aShrinkTimeoutMsec,
+ uint32_t aClearTimeoutMsec,
+ uint32_t aInitialPoolSize,
+ uint32_t aPoolUnusedSize,
+ TextureForwarder* aAllocator);
+
+ /**
+ * Gets an allocated TextureClient of size and format that are determined
+ * by the initialisation parameters given to the pool. This will either be
+ * a cached client that was returned to the pool, or a newly allocated
+ * client if one isn't available.
+ *
+ * All clients retrieved by this method should be returned using the return
+ * functions, or reported lost so that the pool can manage its size correctly.
+ */
+ already_AddRefed<TextureClient> GetTextureClient() override;
+
+ /**
+ * Return a TextureClient that is no longer being used and is ready for
+ * immediate re-use or destruction.
+ */
+ void ReturnTextureClient(TextureClient *aClient);
+
+ /**
+ * Return a TextureClient that is not yet ready to be reused, but will be
+ * imminently.
+ */
+ void ReturnTextureClientDeferred(TextureClient *aClient) override;
+
+ /**
+ * Return any clients to the pool that were previously returned in
+ * ReturnTextureClientDeferred.
+ */
+ void ReturnDeferredClients();
+
+ /**
+ * Attempt to shrink the pool so that there are no more than
+ * mInitialPoolSize outstanding.
+ */
+ void ShrinkToMaximumSize();
+
+ /**
+ * Report that a client retrieved via GetTextureClient() has become
+ * unusable, so that it will no longer be tracked.
+ */
+ virtual void ReportClientLost() override;
+
+ /**
+ * Calling this will cause the pool to attempt to relinquish any unused
+ * clients.
+ */
+ void Clear();
+
+ LayersBackend GetBackend() const { return mBackend; }
+ int32_t GetMaxTextureSize() const { return mMaxTextureSize; }
+ gfx::SurfaceFormat GetFormat() { return mFormat; }
+ TextureFlags GetFlags() const { return mFlags; }
+
+ /**
+ * Clear the pool and put it in a state where it won't recycle any new texture.
+ */
+ void Destroy();
+
+private:
+ void ReturnUnlockedClients();
+
+ /// Allocate a single TextureClient to be returned from the pool.
+ void AllocateTextureClient();
+
+ /// Reset and/or initialise timers for shrinking/clearing the pool.
+ void ResetTimers();
+
+ /// Backend passed to the TextureClient for buffer creation.
+ LayersBackend mBackend;
+
+ // Max texture size passed to the TextureClient for buffer creation.
+ int32_t mMaxTextureSize;
+
+ /// Format is passed to the TextureClient for buffer creation.
+ gfx::SurfaceFormat mFormat;
+
+ /// The width and height of the tiles to be used.
+ gfx::IntSize mSize;
+
+ /// Flags passed to the TextureClient for buffer creation.
+ const TextureFlags mFlags;
+
+ /// How long to wait after a TextureClient is returned before trying
+ /// to shrink the pool to its maximum size of mPoolUnusedSize.
+ uint32_t mShrinkTimeoutMsec;
+
+ /// How long to wait after a TextureClient is returned before trying
+ /// to clear the pool.
+ uint32_t mClearTimeoutMsec;
+
+ // The initial number of unused texture clients to seed the pool with
+ // on construction
+ uint32_t mInitialPoolSize;
+
+ // How many unused texture clients to try and keep around if we go over
+ // the initial allocation
+ uint32_t mPoolUnusedSize;
+
+ /// This is a total number of clients in the wild and in the stack of
+ /// deferred clients (see below). So, the total number of clients in
+ /// existence is always mOutstandingClients + the size of mTextureClients.
+ uint32_t mOutstandingClients;
+
+ // On b2g gonk, std::queue might be a better choice.
+ // On ICS, fence wait happens implicitly before drawing.
+ // Since JB, fence wait happens explicitly when fetching a client from the pool.
+ std::stack<RefPtr<TextureClient> > mTextureClients;
+
+ std::list<RefPtr<TextureClient>> mTextureClientsDeferred;
+ RefPtr<nsITimer> mShrinkTimer;
+ RefPtr<nsITimer> mClearTimer;
+ // This mSurfaceAllocator owns us, so no need to hold a ref to it
+ TextureForwarder* mSurfaceAllocator;
+
+ // Keep track of whether this pool has been destroyed or not. If it has,
+ // we won't accept returns of TextureClients anymore, and the refcounting
+ // should take care of their destruction.
+ bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTURECLIENTPOOL_H */
diff --git a/gfx/layers/client/TextureClientRecycleAllocator.cpp b/gfx/layers/client/TextureClientRecycleAllocator.cpp
new file mode 100644
index 000000000..6a06d3f68
--- /dev/null
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -0,0 +1,279 @@
+/* -*- 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 "gfxPlatform.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "TextureClientRecycleAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+// Used to keep TextureClient's reference count stable as not to disrupt recycling.
+class TextureClientHolder
+{
+ ~TextureClientHolder() {}
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureClientHolder)
+
+ explicit TextureClientHolder(TextureClient* aClient)
+ : mTextureClient(aClient)
+ , mWillRecycle(true)
+ {}
+
+ TextureClient* GetTextureClient()
+ {
+ return mTextureClient;
+ }
+
+ bool WillRecycle()
+ {
+ return mWillRecycle;
+ }
+
+ void ClearWillRecycle()
+ {
+ mWillRecycle = false;
+ }
+
+ void ClearTextureClient() { mTextureClient = nullptr; }
+protected:
+ RefPtr<TextureClient> mTextureClient;
+ bool mWillRecycle;
+};
+
+class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+ DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
+ gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocationFlags)
+ : ITextureClientAllocationHelper(aFormat,
+ aSize,
+ aSelector,
+ aTextureFlags,
+ aAllocationFlags)
+ , mAllocator(aAllocator)
+ {}
+
+ bool IsCompatible(TextureClient* aTextureClient) override
+ {
+ if (aTextureClient->GetFormat() != mFormat ||
+ aTextureClient->GetSize() != mSize) {
+ return false;
+ }
+ return true;
+ }
+
+ already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
+ {
+ return mAllocator->Allocate(mFormat,
+ mSize,
+ mSelector,
+ mTextureFlags,
+ mAllocationFlags);
+ }
+
+protected:
+ TextureClientRecycleAllocator* mAllocator;
+};
+
+YCbCrTextureClientAllocationHelper::YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
+ TextureFlags aTextureFlags)
+ : ITextureClientAllocationHelper(gfx::SurfaceFormat::YUV,
+ aData.mYSize,
+ BackendSelector::Content,
+ aTextureFlags,
+ ALLOC_DEFAULT)
+ , mData(aData)
+{
+}
+
+bool
+YCbCrTextureClientAllocationHelper::IsCompatible(TextureClient* aTextureClient)
+{
+ MOZ_ASSERT(aTextureClient->GetFormat() == gfx::SurfaceFormat::YUV);
+
+ BufferTextureData* bufferData = aTextureClient->GetInternalData()->AsBufferTextureData();
+ if (!bufferData ||
+ aTextureClient->GetSize() != mData.mYSize ||
+ bufferData->GetCbCrSize().isNothing() ||
+ bufferData->GetCbCrSize().ref() != mData.mCbCrSize ||
+ bufferData->GetYUVColorSpace().isNothing() ||
+ bufferData->GetYUVColorSpace().ref() != mData.mYUVColorSpace ||
+ bufferData->GetStereoMode().isNothing() ||
+ bufferData->GetStereoMode().ref() != mData.mStereoMode) {
+ return false;
+ }
+ return true;
+}
+
+already_AddRefed<TextureClient>
+YCbCrTextureClientAllocationHelper::Allocate(KnowsCompositor* aAllocator)
+{
+ return TextureClient::CreateForYCbCr(aAllocator,
+ mData.mYSize, mData.mCbCrSize,
+ mData.mStereoMode,
+ mData.mYUVColorSpace,
+ mTextureFlags);
+}
+
+TextureClientRecycleAllocator::TextureClientRecycleAllocator(KnowsCompositor* aAllocator)
+ : mSurfaceAllocator(aAllocator)
+ , mMaxPooledSize(kMaxPooledSized)
+ , mLock("TextureClientRecycleAllocatorImp.mLock")
+ , mIsDestroyed(false)
+{
+}
+
+TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ MOZ_ASSERT(mInUseClients.empty());
+}
+
+void
+TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
+{
+ mMaxPooledSize = aMax;
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
+ DefaultTextureClientAllocationHelper helper(this,
+ aFormat,
+ aSize,
+ aSelector,
+ aTextureFlags,
+ aAllocFlags);
+ return CreateOrRecycle(helper);
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
+{
+ MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
+
+ RefPtr<TextureClientHolder> textureHolder;
+
+ {
+ MutexAutoLock lock(mLock);
+ if (mIsDestroyed) {
+ return nullptr;
+ }
+ if (!mPooledClients.empty()) {
+ textureHolder = mPooledClients.top();
+ mPooledClients.pop();
+ // If a pooled TextureClient is not compatible, release it.
+ if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
+ // Release TextureClient.
+ RefPtr<Runnable> task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
+ textureHolder->ClearTextureClient();
+ textureHolder = nullptr;
+ mSurfaceAllocator->GetTextureForwarder()->GetMessageLoop()->PostTask(task.forget());
+ } else {
+ textureHolder->GetTextureClient()->RecycleTexture(aHelper.mTextureFlags);
+ }
+ }
+ }
+
+ if (!textureHolder) {
+ // Allocate new TextureClient
+ RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
+ if (!texture) {
+ return nullptr;
+ }
+ textureHolder = new TextureClientHolder(texture);
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(mInUseClients.find(textureHolder->GetTextureClient()) == mInUseClients.end());
+ // Register TextureClient
+ mInUseClients[textureHolder->GetTextureClient()] = textureHolder;
+ }
+ RefPtr<TextureClient> client(textureHolder->GetTextureClient());
+
+ // Make sure the texture holds a reference to us, and ask it to call RecycleTextureClient when its
+ // ref count drops to 1.
+ client->SetRecycleAllocator(this);
+ return client.forget();
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags)
+{
+ return TextureClient::CreateForDrawing(mSurfaceAllocator, aFormat, aSize,
+ aSelector, aTextureFlags, aAllocFlags);
+}
+
+void
+TextureClientRecycleAllocator::ShrinkToMinimumSize()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ // We can not clear using TextureClients safely.
+ // Just clear WillRecycle here.
+ std::map<TextureClient*, RefPtr<TextureClientHolder> >::iterator it;
+ for (it = mInUseClients.begin(); it != mInUseClients.end(); it++) {
+ RefPtr<TextureClientHolder> holder = it->second;
+ holder->ClearWillRecycle();
+ }
+}
+
+void
+TextureClientRecycleAllocator::Destroy()
+{
+ MutexAutoLock lock(mLock);
+ while (!mPooledClients.empty()) {
+ mPooledClients.pop();
+ }
+ mIsDestroyed = true;
+}
+
+void
+TextureClientRecycleAllocator::RecycleTextureClient(TextureClient* aClient)
+{
+ // Clearing the recycle allocator drops a reference, so make sure we stay alive
+ // for the duration of this function.
+ RefPtr<TextureClientRecycleAllocator> kungFuDeathGrip(this);
+ aClient->SetRecycleAllocator(nullptr);
+
+ RefPtr<TextureClientHolder> textureHolder;
+ {
+ MutexAutoLock lock(mLock);
+ if (mInUseClients.find(aClient) != mInUseClients.end()) {
+ textureHolder = mInUseClients[aClient]; // Keep reference count of TextureClientHolder within lock.
+ if (textureHolder->WillRecycle() &&
+ !mIsDestroyed && mPooledClients.size() < mMaxPooledSize) {
+ mPooledClients.push(textureHolder);
+ }
+ mInUseClients.erase(aClient);
+ }
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientRecycleAllocator.h b/gfx/layers/client/TextureClientRecycleAllocator.h
new file mode 100644
index 000000000..23ba78991
--- /dev/null
+++ b/gfx/layers/client/TextureClientRecycleAllocator.h
@@ -0,0 +1,140 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
+#define MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H
+
+#include <map>
+#include <stack>
+#include "mozilla/gfx/Types.h"
+#include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/RefPtr.h"
+#include "TextureClient.h"
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+namespace layers {
+
+class TextureClientHolder;
+struct PlanarYCbCrData;
+
+class ITextureClientRecycleAllocator
+{
+protected:
+ virtual ~ITextureClientRecycleAllocator() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ITextureClientRecycleAllocator)
+
+protected:
+ friend class TextureClient;
+ virtual void RecycleTextureClient(TextureClient* aClient) = 0;
+};
+
+class ITextureClientAllocationHelper
+{
+public:
+ ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocationFlags)
+ : mFormat(aFormat)
+ , mSize(aSize)
+ , mSelector(aSelector)
+ , mTextureFlags(aTextureFlags | TextureFlags::RECYCLE) // Set recycle flag
+ , mAllocationFlags(aAllocationFlags)
+ {}
+
+ virtual already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) = 0;
+ virtual bool IsCompatible(TextureClient* aTextureClient) = 0;
+
+ const gfx::SurfaceFormat mFormat;
+ const gfx::IntSize mSize;
+ const BackendSelector mSelector;
+ const TextureFlags mTextureFlags;
+ const TextureAllocationFlags mAllocationFlags;
+};
+
+class YCbCrTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+ YCbCrTextureClientAllocationHelper(const PlanarYCbCrData& aData,
+ TextureFlags aTextureFlags);
+
+ bool IsCompatible(TextureClient* aTextureClient) override;
+
+ already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override;
+
+protected:
+ const PlanarYCbCrData& mData;
+};
+
+
+/**
+ * TextureClientRecycleAllocator provides TextureClients allocation and
+ * recycling capabilities. It expects allocations of same sizes and
+ * attributres. If a recycled TextureClient is different from
+ * requested one, the recycled one is dropped and new TextureClient is allocated.
+ *
+ * By default this uses TextureClient::CreateForDrawing to allocate new texture
+ * clients.
+ */
+class TextureClientRecycleAllocator : public ITextureClientRecycleAllocator
+{
+protected:
+ virtual ~TextureClientRecycleAllocator();
+
+public:
+ explicit TextureClientRecycleAllocator(KnowsCompositor* aAllocator);
+
+ void SetMaxPoolSize(uint32_t aMax);
+
+ // Creates and allocates a TextureClient.
+ already_AddRefed<TextureClient>
+ CreateOrRecycle(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags flags = ALLOC_DEFAULT);
+
+ already_AddRefed<TextureClient>
+ CreateOrRecycle(ITextureClientAllocationHelper& aHelper);
+
+ void ShrinkToMinimumSize();
+
+ void Destroy();
+
+protected:
+ virtual already_AddRefed<TextureClient>
+ Allocate(gfx::SurfaceFormat aFormat,
+ gfx::IntSize aSize,
+ BackendSelector aSelector,
+ TextureFlags aTextureFlags,
+ TextureAllocationFlags aAllocFlags);
+
+ RefPtr<KnowsCompositor> mSurfaceAllocator;
+
+ friend class DefaultTextureClientAllocationHelper;
+ void RecycleTextureClient(TextureClient* aClient) override;
+
+ static const uint32_t kMaxPooledSized = 2;
+ uint32_t mMaxPooledSize;
+
+ std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
+
+ // On b2g gonk, std::queue might be a better choice.
+ // On ICS, fence wait happens implicitly before drawing.
+ // Since JB, fence wait happens explicitly when fetching a client from the pool.
+ // stack is good from Graphics cache usage point of view.
+ std::stack<RefPtr<TextureClientHolder> > mPooledClients;
+ Mutex mLock;
+ bool mIsDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_TEXTURECLIENT_RECYCLE_ALLOCATOR_H */
diff --git a/gfx/layers/client/TextureClientSharedSurface.cpp b/gfx/layers/client/TextureClientSharedSurface.cpp
new file mode 100644
index 000000000..b0cbfec21
--- /dev/null
+++ b/gfx/layers/client/TextureClientSharedSurface.cpp
@@ -0,0 +1,96 @@
+/* -*- 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 "TextureClientSharedSurface.h"
+
+#include "GLContext.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Logging.h" // for gfxDebug
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/Unused.h"
+#include "nsThreadUtils.h"
+#include "SharedSurface.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+
+SharedSurfaceTextureData::SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf)
+ : mSurf(Move(surf))
+{}
+
+SharedSurfaceTextureData::~SharedSurfaceTextureData()
+{}
+
+void
+SharedSurfaceTextureData::Deallocate(LayersIPCChannel*)
+{}
+
+void
+SharedSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = mSurf->mSize;
+ aInfo.format = gfx::SurfaceFormat::UNKNOWN;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = false;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+SharedSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ return mSurf->ToSurfaceDescriptor(&aOutDescriptor);
+}
+
+
+SharedSurfaceTextureClient::SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator)
+: TextureClient(aData, aFlags, aAllocator)
+{
+ mWorkaroundAnnoyingSharedSurfaceLifetimeIssues = true;
+}
+
+already_AddRefed<SharedSurfaceTextureClient>
+SharedSurfaceTextureClient::Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags)
+{
+ if (!surf) {
+ return nullptr;
+ }
+ TextureFlags flags = aFlags | TextureFlags::RECYCLE | surf->GetTextureFlags();
+ SharedSurfaceTextureData* data = new SharedSurfaceTextureData(Move(surf));
+ return MakeAndAddRef<SharedSurfaceTextureClient>(data, flags, aAllocator);
+}
+
+SharedSurfaceTextureClient::~SharedSurfaceTextureClient()
+{
+ // XXX - Things break when using the proper destruction handshake with
+ // SharedSurfaceTextureData because the TextureData outlives its gl
+ // context. Having a strong reference to the gl context creates a cycle.
+ // This needs to be fixed in a better way, though, because deleting
+ // the TextureData here can race with the compositor and cause flashing.
+ TextureData* data = mData;
+ mData = nullptr;
+
+ Destroy();
+
+ if (data) {
+ // Destroy mData right away without doing the proper deallocation handshake,
+ // because SharedSurface depends on things that may not outlive the texture's
+ // destructor so we can't wait until we know the compositor isn't using the
+ // texture anymore.
+ // It goes without saying that this is really bad and we should fix the bugs
+ // that block doing the right thing such as bug 1224199 sooner rather than
+ // later.
+ delete data;
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TextureClientSharedSurface.h b/gfx/layers/client/TextureClientSharedSurface.h
new file mode 100644
index 000000000..cfe5085f8
--- /dev/null
+++ b/gfx/layers/client/TextureClientSharedSurface.h
@@ -0,0 +1,77 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
+#define MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
+
+#include <cstddef> // for size_t
+#include <stdint.h> // for uint32_t, uint8_t, uint64_t
+#include "GLContextTypes.h" // for GLContext (ptr only), etc
+#include "TextureClient.h"
+#include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
+#include "mozilla/RefPtr.h" // for RefPtr, RefCounted
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+class SharedSurface;
+class SurfaceFactory;
+} // namespace gl
+
+namespace layers {
+
+class SharedSurfaceTextureClient;
+
+class SharedSurfaceTextureData : public TextureData
+{
+protected:
+ const UniquePtr<gl::SharedSurface> mSurf;
+
+ friend class SharedSurfaceTextureClient;
+
+ explicit SharedSurfaceTextureData(UniquePtr<gl::SharedSurface> surf);
+public:
+
+ ~SharedSurfaceTextureData();
+
+ virtual bool Lock(OpenMode) override { return false; }
+
+ virtual void Unlock() override {}
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ gl::SharedSurface* Surf() const { return mSurf.get(); }
+};
+
+class SharedSurfaceTextureClient : public TextureClient
+{
+public:
+ SharedSurfaceTextureClient(SharedSurfaceTextureData* aData,
+ TextureFlags aFlags,
+ LayersIPCChannel* aAllocator);
+
+ ~SharedSurfaceTextureClient();
+
+ static already_AddRefed<SharedSurfaceTextureClient>
+ Create(UniquePtr<gl::SharedSurface> surf, gl::SurfaceFactory* factory,
+ LayersIPCChannel* aAllocator, TextureFlags aFlags);
+
+ gl::SharedSurface* Surf() const {
+ return static_cast<const SharedSurfaceTextureData*>(GetInternalData())->Surf();
+ }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_TEXTURECLIENT_SHAREDSURFACE_H
diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp
new file mode 100644
index 000000000..c5677a8a3
--- /dev/null
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -0,0 +1,1430 @@
+/* -*- 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/TiledContentClient.h"
+#include <math.h> // for ceil, ceilf, floor
+#include <algorithm>
+#include "ClientTiledPaintedLayer.h" // for ClientTiledPaintedLayer
+#include "GeckoProfiler.h" // for PROFILER_LABEL
+#include "ClientLayerManager.h" // for ClientLayerManager
+#include "gfxContext.h" // for gfxContext, etc
+#include "gfxPlatform.h" // for gfxPlatform
+#include "gfxPrefs.h" // for gfxPrefs
+#include "gfxRect.h" // for gfxRect
+#include "mozilla/MathAlgorithms.h" // for Abs
+#include "mozilla/gfx/Point.h" // for IntSize
+#include "mozilla/gfx/Rect.h" // for Rect
+#include "mozilla/gfx/Tools.h" // for BytesPerPixel
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder
+#include "TextureClientPool.h"
+#include "nsDebug.h" // for NS_ASSERTION
+#include "nsISupportsImpl.h" // for gfxContext::AddRef, etc
+#include "nsExpirationTracker.h" // for nsExpirationTracker
+#include "nsMathUtils.h" // for NS_lroundf
+#include "LayersLogging.h"
+#include "UnitTransforms.h" // for TransformTo
+#include "mozilla/UniquePtr.h"
+
+// This is the minimum area that we deem reasonable to copy from the front buffer to the
+// back buffer on tile updates. If the valid region is smaller than this, we just
+// redraw it and save on the copy (and requisite surface-locking involved).
+#define MINIMUM_TILE_COPY_AREA (1.f/16.f)
+
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+#include "cairo.h"
+#include <sstream>
+using mozilla::layers::Layer;
+static void DrawDebugOverlay(mozilla::gfx::DrawTarget* dt, int x, int y, int width, int height)
+{
+ gfxContext c(dt);
+
+ // Draw border
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(0, 0, width, height));
+ c.Stroke();
+
+ // Build tile description
+ std::stringstream ss;
+ ss << x << ", " << y;
+
+ // Draw text using cairo toy text API
+ // XXX: this drawing will silently fail if |dt| doesn't have a Cairo backend
+ cairo_t* cr = gfxFont::RefCairo(dt);
+ cairo_set_font_size(cr, 25);
+ cairo_text_extents_t extents;
+ cairo_text_extents(cr, ss.str().c_str(), &extents);
+
+ int textWidth = extents.width + 6;
+
+ c.NewPath();
+ c.SetDeviceColor(Color(0.f, 0.f, 0.f));
+ c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
+ c.Fill();
+
+ c.NewPath();
+ c.SetDeviceColor(Color(1.0, 0.0, 0.0));
+ c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 30)));
+ c.Stroke();
+
+ c.NewPath();
+ cairo_move_to(cr, 4, 28);
+ cairo_show_text(cr, ss.str().c_str());
+
+}
+
+#endif
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+
+MultiTiledContentClient::MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager)
+ : TiledContentClient(aManager, "Multi")
+ , mTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
+ , mLowPrecisionTiledBuffer(aPaintedLayer, *this, aManager, &mSharedFrameMetricsHelper)
+{
+ MOZ_COUNT_CTOR(MultiTiledContentClient);
+ mLowPrecisionTiledBuffer.SetResolution(gfxPrefs::LowPrecisionResolution());
+ mHasLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
+}
+
+void
+MultiTiledContentClient::ClearCachedResources()
+{
+ CompositableClient::ClearCachedResources();
+ mTiledBuffer.DiscardBuffers();
+ mLowPrecisionTiledBuffer.DiscardBuffers();
+}
+
+void
+MultiTiledContentClient::UpdatedBuffer(TiledBufferType aType)
+{
+ ClientMultiTiledLayerBuffer* buffer = aType == LOW_PRECISION_TILED_BUFFER
+ ? &mLowPrecisionTiledBuffer
+ : &mTiledBuffer;
+
+ MOZ_ASSERT(aType != LOW_PRECISION_TILED_BUFFER || mHasLowPrecision);
+
+ mForwarder->UseTiledLayerBuffer(this, buffer->GetSurfaceDescriptorTiles());
+ buffer->ClearPaintedRegion();
+}
+
+SharedFrameMetricsHelper::SharedFrameMetricsHelper()
+ : mLastProgressiveUpdateWasLowPrecision(false)
+ , mProgressiveUpdateWasInDanger(false)
+{
+ MOZ_COUNT_CTOR(SharedFrameMetricsHelper);
+}
+
+SharedFrameMetricsHelper::~SharedFrameMetricsHelper()
+{
+ MOZ_COUNT_DTOR(SharedFrameMetricsHelper);
+}
+
+static inline bool
+FuzzyEquals(float a, float b) {
+ return (fabsf(a - b) < 1e-6);
+}
+
+static AsyncTransform
+ComputeViewTransform(const FrameMetrics& aContentMetrics, const FrameMetrics& aCompositorMetrics)
+{
+ // This is basically the same code as AsyncPanZoomController::GetCurrentAsyncTransform
+ // but with aContentMetrics used in place of mLastContentPaintMetrics, because they
+ // should be equivalent, modulo race conditions while transactions are inflight.
+
+ ParentLayerPoint translation = (aCompositorMetrics.GetScrollOffset() - aContentMetrics.GetScrollOffset())
+ * aCompositorMetrics.GetZoom();
+ return AsyncTransform(aCompositorMetrics.GetAsyncZoom(), -translation);
+}
+
+bool
+SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics(
+ const LayerMetricsWrapper& aLayer,
+ bool aHasPendingNewThebesContent,
+ bool aLowPrecision,
+ AsyncTransform& aViewTransform)
+{
+ MOZ_ASSERT(aLayer);
+
+ CompositorBridgeChild* compositor = nullptr;
+ if (aLayer.Manager() &&
+ aLayer.Manager()->AsClientLayerManager()) {
+ compositor = aLayer.Manager()->AsClientLayerManager()->GetCompositorBridgeChild();
+ }
+
+ if (!compositor) {
+ return false;
+ }
+
+ const FrameMetrics& contentMetrics = aLayer.Metrics();
+ FrameMetrics compositorMetrics;
+
+ if (!compositor->LookupCompositorFrameMetrics(contentMetrics.GetScrollId(),
+ compositorMetrics)) {
+ return false;
+ }
+
+ aViewTransform = ComputeViewTransform(contentMetrics, compositorMetrics);
+
+ // Reset the checkerboard risk flag when switching to low precision
+ // rendering.
+ if (aLowPrecision && !mLastProgressiveUpdateWasLowPrecision) {
+ // Skip low precision rendering until we're at risk of checkerboarding.
+ if (!mProgressiveUpdateWasInDanger) {
+ TILING_LOG("TILING: Aborting low-precision rendering because not at risk of checkerboarding\n");
+ return true;
+ }
+ mProgressiveUpdateWasInDanger = false;
+ }
+ mLastProgressiveUpdateWasLowPrecision = aLowPrecision;
+
+ // Always abort updates if the resolution has changed. There's no use
+ // in drawing at the incorrect resolution.
+ if (!FuzzyEquals(compositorMetrics.GetZoom().xScale, contentMetrics.GetZoom().xScale) ||
+ !FuzzyEquals(compositorMetrics.GetZoom().yScale, contentMetrics.GetZoom().yScale)) {
+ TILING_LOG("TILING: Aborting because resolution changed from %s to %s\n",
+ ToString(contentMetrics.GetZoom()).c_str(), ToString(compositorMetrics.GetZoom()).c_str());
+ return true;
+ }
+
+ // Never abort drawing if we can't be sure we've sent a more recent
+ // display-port. If we abort updating when we shouldn't, we can end up
+ // with blank regions on the screen and we open up the risk of entering
+ // an endless updating cycle.
+ if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
+ fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().x - compositorMetrics.GetDisplayPort().x) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().y - compositorMetrics.GetDisplayPort().y) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().width - compositorMetrics.GetDisplayPort().width) <= 2 &&
+ fabsf(contentMetrics.GetDisplayPort().height - compositorMetrics.GetDisplayPort().height) <= 2) {
+ return false;
+ }
+
+ // When not a low precision pass and the page is in danger of checker boarding
+ // abort update.
+ if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
+ bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
+ contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
+ // If scrollUpdatePending is true, then that means the content-side
+ // metrics has a new scroll offset that is going to be forced into the
+ // compositor but it hasn't gotten there yet.
+ // Even though right now comparing the metrics might indicate we're
+ // about to checkerboard (and that's true), the checkerboarding will
+ // disappear as soon as the new scroll offset update is processed
+ // on the compositor side. To avoid leaving things in a low-precision
+ // paint, we need to detect and handle this case (bug 1026756).
+ if (!scrollUpdatePending && AboutToCheckerboard(contentMetrics, compositorMetrics)) {
+ mProgressiveUpdateWasInDanger = true;
+ return true;
+ }
+ }
+
+ // Abort drawing stale low-precision content if there's a more recent
+ // display-port in the pipeline.
+ if (aLowPrecision && !aHasPendingNewThebesContent) {
+ TILING_LOG("TILING: Aborting low-precision because of new pending content\n");
+ return true;
+ }
+
+ return false;
+}
+
+bool
+SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics)
+{
+ // The size of the painted area is originally computed in layer pixels in layout, but then
+ // converted to app units and then back to CSS pixels before being put in the FrameMetrics.
+ // This process can introduce some rounding error, so we inflate the rect by one app unit
+ // to account for that.
+ CSSRect painted = (aContentMetrics.GetCriticalDisplayPort().IsEmpty()
+ ? aContentMetrics.GetDisplayPort()
+ : aContentMetrics.GetCriticalDisplayPort())
+ + aContentMetrics.GetScrollOffset();
+ painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
+
+ // Inflate the rect by the danger zone. See the description of the danger zone prefs
+ // in AsyncPanZoomController.cpp for an explanation of this.
+ CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
+ aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
+ showing.Inflate(LayerSize(gfxPrefs::APZDangerZoneX(), gfxPrefs::APZDangerZoneY())
+ / aCompositorMetrics.LayersPixelsPerCSSPixel());
+
+ // Clamp both rects to the scrollable rect, because having either of those
+ // exceed the scrollable rect doesn't make sense, and could lead to false
+ // positives.
+ painted = painted.Intersect(aContentMetrics.GetScrollableRect());
+ showing = showing.Intersect(aContentMetrics.GetScrollableRect());
+
+ if (!painted.Contains(showing)) {
+ TILING_LOG("TILING: About to checkerboard; content %s\n", Stringify(aContentMetrics).c_str());
+ TILING_LOG("TILING: About to checkerboard; painted %s\n", Stringify(painted).c_str());
+ TILING_LOG("TILING: About to checkerboard; compositor %s\n", Stringify(aCompositorMetrics).c_str());
+ TILING_LOG("TILING: About to checkerboard; showing %s\n", Stringify(showing).c_str());
+ return true;
+ }
+ return false;
+}
+
+ClientMultiTiledLayerBuffer::ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager,
+ SharedFrameMetricsHelper* aHelper)
+ : ClientTiledLayerBuffer(aPaintedLayer, aCompositableClient)
+ , mManager(aManager)
+ , mCallback(nullptr)
+ , mCallbackData(nullptr)
+ , mSharedFrameMetricsHelper(aHelper)
+{
+}
+
+bool
+ClientTiledLayerBuffer::HasFormatChanged() const
+{
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+ return content != mLastPaintContentType ||
+ mode != mLastPaintSurfaceMode;
+}
+
+
+gfxContentType
+ClientTiledLayerBuffer::GetContentType(SurfaceMode* aMode) const
+{
+ gfxContentType content =
+ mPaintedLayer.CanUseOpaqueSurface() ? gfxContentType::COLOR :
+ gfxContentType::COLOR_ALPHA;
+ SurfaceMode mode = mPaintedLayer.GetSurfaceMode();
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+#else
+ if (!mPaintedLayer.GetParent() ||
+ !mPaintedLayer.GetParent()->SupportsComponentAlphaChildren()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ } else {
+ content = gfxContentType::COLOR;
+ }
+#endif
+ } else if (mode == SurfaceMode::SURFACE_OPAQUE) {
+#if defined(MOZ_GFX_OPTIMIZE_MOBILE)
+ if (IsLowPrecision()) {
+ // If we're in low-res mode, drawing can sample from outside the visible
+ // region. Make sure that we only sample transparency if that happens.
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+#else
+ if (mPaintedLayer.MayResample()) {
+ mode = SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA;
+ content = gfxContentType::COLOR_ALPHA;
+ }
+#endif
+ }
+
+ if (aMode) {
+ *aMode = mode;
+ }
+ return content;
+}
+
+class TileExpiry final : public nsExpirationTracker<TileClient, 3>
+{
+ public:
+ TileExpiry() : nsExpirationTracker<TileClient, 3>(1000, "TileExpiry") {}
+
+ static void AddTile(TileClient* aTile)
+ {
+ if (!sTileExpiry) {
+ sTileExpiry = MakeUnique<TileExpiry>();
+ }
+
+ sTileExpiry->AddObject(aTile);
+ }
+
+ static void RemoveTile(TileClient* aTile)
+ {
+ MOZ_ASSERT(sTileExpiry);
+ sTileExpiry->RemoveObject(aTile);
+ }
+
+ static void Shutdown() {
+ sTileExpiry = nullptr;
+ }
+ private:
+ virtual void NotifyExpired(TileClient* aTile) override
+ {
+ aTile->DiscardBackBuffer();
+ }
+
+ static UniquePtr<TileExpiry> sTileExpiry;
+};
+UniquePtr<TileExpiry> TileExpiry::sTileExpiry;
+
+void ShutdownTileCache()
+{
+ TileExpiry::Shutdown();
+}
+
+void
+TileClient::PrivateProtector::Set(TileClient * const aContainer, RefPtr<TextureClient> aNewValue)
+{
+ if (mBuffer) {
+ TileExpiry::RemoveTile(aContainer);
+ }
+ mBuffer = aNewValue;
+ if (mBuffer) {
+ TileExpiry::AddTile(aContainer);
+ }
+}
+
+void
+TileClient::PrivateProtector::Set(TileClient * const aContainer, TextureClient* aNewValue)
+{
+ Set(aContainer, RefPtr<TextureClient>(aNewValue));
+}
+
+// Placeholder
+TileClient::TileClient()
+ : mWasPlaceholder(false)
+{
+}
+
+TileClient::~TileClient()
+{
+ if (mExpirationState.IsTracked()) {
+ MOZ_ASSERT(mBackBuffer);
+ TileExpiry::RemoveTile(this);
+ }
+}
+
+TileClient::TileClient(const TileClient& o)
+{
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+}
+
+TileClient&
+TileClient::operator=(const TileClient& o)
+{
+ if (this == &o) return *this;
+ mBackBuffer.Set(this, o.mBackBuffer);
+ mBackBufferOnWhite = o.mBackBufferOnWhite;
+ mFrontBuffer = o.mFrontBuffer;
+ mFrontBufferOnWhite = o.mFrontBufferOnWhite;
+ mWasPlaceholder = o.mWasPlaceholder;
+ mUpdateRect = o.mUpdateRect;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ mLastUpdate = o.mLastUpdate;
+#endif
+ mAllocator = o.mAllocator;
+ mInvalidFront = o.mInvalidFront;
+ mInvalidBack = o.mInvalidBack;
+ return *this;
+}
+
+void
+TileClient::Dump(std::stringstream& aStream)
+{
+ aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer << " fb=" << mFrontBuffer.get();
+ if (mBackBufferOnWhite) {
+ aStream << " bbow=" << mBackBufferOnWhite.get();
+ }
+ if (mFrontBufferOnWhite) {
+ aStream << " fbow=" << mFrontBufferOnWhite.get();
+ }
+ aStream << ")";
+}
+
+void
+TileClient::Flip()
+{
+ RefPtr<TextureClient> frontBuffer = mFrontBuffer;
+ RefPtr<TextureClient> frontBufferOnWhite = mFrontBufferOnWhite;
+ mFrontBuffer = mBackBuffer;
+ mFrontBufferOnWhite = mBackBufferOnWhite;
+ mBackBuffer.Set(this, frontBuffer);
+ mBackBufferOnWhite = frontBufferOnWhite;
+ nsIntRegion invalidFront = mInvalidFront;
+ mInvalidFront = mInvalidBack;
+ mInvalidBack = invalidFront;
+}
+
+static bool
+CopyFrontToBack(TextureClient* aFront,
+ TextureClient* aBack,
+ const gfx::IntRect& aRectToCopy)
+{
+ TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
+ if (!frontLock.Succeeded()) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's front buffer";
+ return false;
+ }
+
+ if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
+ return false;
+ }
+
+ gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
+ aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+ return true;
+}
+
+void
+TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
+ nsIntRegion& aAddPaintedRegion)
+{
+ if (mBackBuffer && mFrontBuffer) {
+ gfx::IntSize tileSize = mFrontBuffer->GetSize();
+ const IntRect tileRect = IntRect(0, 0, tileSize.width, tileSize.height);
+
+ if (aDirtyRegion.Contains(tileRect)) {
+ // The dirty region means that we no longer need the front buffer, so
+ // discard it.
+ DiscardFrontBuffer();
+ } else {
+ // Region that needs copying.
+ nsIntRegion regionToCopy = mInvalidBack;
+
+ regionToCopy.Sub(regionToCopy, aDirtyRegion);
+
+ aAddPaintedRegion = regionToCopy;
+
+ if (regionToCopy.IsEmpty()) {
+ // Just redraw it all.
+ return;
+ }
+
+ // Copy the bounding rect of regionToCopy. As tiles are quite small, it
+ // is unlikely that we'd save much by copying each individual rect of the
+ // region, but we can reevaluate this if it becomes an issue.
+ const IntRect rectToCopy = regionToCopy.GetBounds();
+ gfx::IntRect gfxRectToCopy(rectToCopy.x, rectToCopy.y, rectToCopy.width, rectToCopy.height);
+ CopyFrontToBack(mFrontBuffer, mBackBuffer, gfxRectToCopy);
+
+ if (mBackBufferOnWhite) {
+ MOZ_ASSERT(mFrontBufferOnWhite);
+ CopyFrontToBack(mFrontBufferOnWhite, mBackBufferOnWhite, gfxRectToCopy);
+ }
+
+ mInvalidBack.SetEmpty();
+ }
+ }
+}
+
+void
+TileClient::DiscardFrontBuffer()
+{
+ if (mFrontBuffer) {
+ MOZ_ASSERT(mFrontBuffer->GetReadLock());
+
+ MOZ_ASSERT(mAllocator);
+ if (mAllocator) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBuffer);
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReturnTextureClientDeferred(mFrontBufferOnWhite);
+ }
+ }
+
+ if (mFrontBuffer->IsLocked()) {
+ mFrontBuffer->Unlock();
+ }
+ if (mFrontBufferOnWhite && mFrontBufferOnWhite->IsLocked()) {
+ mFrontBufferOnWhite->Unlock();
+ }
+ mFrontBuffer = nullptr;
+ mFrontBufferOnWhite = nullptr;
+ }
+}
+
+static void
+DiscardTexture(TextureClient* aTexture, TextureClientAllocator* aAllocator)
+{
+ MOZ_ASSERT(aAllocator);
+ if (aTexture && aAllocator) {
+ MOZ_ASSERT(aTexture->GetReadLock());
+ if (!aTexture->HasSynchronization() && aTexture->IsReadLocked()) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ } else {
+ aAllocator->ReturnTextureClientDeferred(aTexture);
+ }
+ if (aTexture->IsLocked()) {
+ aTexture->Unlock();
+ }
+ }
+}
+
+void
+TileClient::DiscardBackBuffer()
+{
+ if (mBackBuffer) {
+ DiscardTexture(mBackBuffer, mAllocator);
+ mBackBuffer.Set(this, nullptr);
+ DiscardTexture(mBackBufferOnWhite, mAllocator);
+ mBackBufferOnWhite = nullptr;
+ }
+}
+
+static already_AddRefed<TextureClient>
+CreateBackBufferTexture(TextureClient* aCurrentTexture,
+ CompositableClient& aCompositable,
+ TextureClientAllocator* aAllocator)
+{
+ if (aCurrentTexture) {
+ // Our current back-buffer is still locked by the compositor. This can occur
+ // when the client is producing faster than the compositor can consume. In
+ // this case we just want to drop it and not return it to the pool.
+ aAllocator->ReportClientLost();
+ }
+
+ RefPtr<TextureClient> texture = aAllocator->GetTextureClient();
+
+ if (!texture) {
+ gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
+ return nullptr;
+ }
+
+ texture->EnableReadLock();
+
+ if (!aCompositable.AddTextureClient(texture)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient";
+ return nullptr;
+ }
+
+ return texture.forget();
+}
+
+TextureClient*
+TileClient::GetBackBuffer(CompositableClient& aCompositable,
+ const nsIntRegion& aDirtyRegion,
+ gfxContentType aContent,
+ SurfaceMode aMode,
+ nsIntRegion& aAddPaintedRegion,
+ RefPtr<TextureClient>* aBackBufferOnWhite)
+{
+ if (!mAllocator) {
+ gfxCriticalError() << "[TileClient] Missing TextureClientAllocator.";
+ return nullptr;
+ }
+ if (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ // It can happen that a component-alpha layer stops being on component alpha
+ // on the next frame, just drop the buffers on white if that happens.
+ if (mBackBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mBackBufferOnWhite = nullptr;
+ }
+ if (mFrontBufferOnWhite) {
+ mAllocator->ReportClientLost();
+ mFrontBufferOnWhite = nullptr;
+ }
+ }
+
+ // Try to re-use the front-buffer if possible
+ if (mFrontBuffer &&
+ mFrontBuffer->HasIntermediateBuffer() &&
+ !mFrontBuffer->IsReadLocked() &&
+ (aMode != SurfaceMode::SURFACE_COMPONENT_ALPHA || (
+ mFrontBufferOnWhite && !mFrontBufferOnWhite->IsReadLocked()))) {
+ // If we had a backbuffer we no longer care about it since we'll
+ // re-use the front buffer.
+ DiscardBackBuffer();
+ Flip();
+ } else {
+ if (!mBackBuffer || mBackBuffer->IsReadLocked()) {
+ mBackBuffer.Set(this,
+ CreateBackBufferTexture(mBackBuffer, aCompositable, mAllocator)
+ );
+ if (!mBackBuffer) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBuffer->GetSize());
+ }
+
+ if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA
+ && (!mBackBufferOnWhite || mBackBufferOnWhite->IsReadLocked())) {
+ mBackBufferOnWhite = CreateBackBufferTexture(
+ mBackBufferOnWhite, aCompositable, mAllocator
+ );
+ if (!mBackBufferOnWhite) {
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ mInvalidBack = IntRect(IntPoint(), mBackBufferOnWhite->GetSize());
+ }
+
+ ValidateBackBufferFromFront(aDirtyRegion, aAddPaintedRegion);
+ }
+
+ if (!mBackBuffer->IsLocked()) {
+ if (!mBackBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (B)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ }
+
+ if (mBackBufferOnWhite && !mBackBufferOnWhite->IsLocked()) {
+ if (!mBackBufferOnWhite->Lock(OpenMode::OPEN_READ_WRITE)) {
+ gfxCriticalError() << "[Tiling:Client] Failed to lock a tile (W)";
+ DiscardBackBuffer();
+ DiscardFrontBuffer();
+ return nullptr;
+ }
+ }
+
+ *aBackBufferOnWhite = mBackBufferOnWhite;
+ return mBackBuffer;
+}
+
+TileDescriptor
+TileClient::GetTileDescriptor()
+{
+ if (IsPlaceholderTile()) {
+ mWasPlaceholder = true;
+ return PlaceholderTileDescriptor();
+ }
+ bool wasPlaceholder = mWasPlaceholder;
+ mWasPlaceholder = false;
+
+ ReadLockDescriptor lock;
+ mFrontBuffer->SerializeReadLock(lock);
+
+ ReadLockDescriptor lockOnWhite = null_t();
+ if (mFrontBufferOnWhite) {
+ mFrontBufferOnWhite->SerializeReadLock(lockOnWhite);
+ }
+
+ return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+ mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+ mUpdateRect,
+ lock, lockOnWhite,
+ wasPlaceholder);
+}
+
+void
+ClientMultiTiledLayerBuffer::DiscardBuffers()
+{
+ for (TileClient& tile : mRetainedTiles) {
+ tile.DiscardBuffers();
+ }
+}
+
+SurfaceDescriptorTiles
+ClientMultiTiledLayerBuffer::GetSurfaceDescriptorTiles()
+{
+ InfallibleTArray<TileDescriptor> tiles;
+
+ for (TileClient& tile : mRetainedTiles) {
+ TileDescriptor tileDesc = tile.GetTileDescriptor();
+ tiles.AppendElement(tileDesc);
+ // Reset the update rect
+ tile.mUpdateRect = IntRect();
+ }
+ return SurfaceDescriptorTiles(mValidRegion,
+ tiles,
+ mTileOrigin, mTileSize,
+ mTiles.mFirst.x, mTiles.mFirst.y,
+ mTiles.mSize.width, mTiles.mSize.height,
+ mResolution, mFrameResolution.xScale,
+ mFrameResolution.yScale,
+ mWasLastPaintProgressive);
+}
+
+void
+ClientMultiTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive)
+{
+ TILING_LOG("TILING %p: PaintThebes painting region %s\n", &mPaintedLayer, Stringify(aPaintRegion).c_str());
+ TILING_LOG("TILING %p: PaintThebes new valid region %s\n", &mPaintedLayer, Stringify(aNewValidRegion).c_str());
+
+ mCallback = aCallback;
+ mCallbackData = aCallbackData;
+ mWasLastPaintProgressive = aIsProgressive;
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ long start = PR_IntervalNow();
+#endif
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (PR_IntervalNow() - start > 30) {
+ const IntRect bounds = aPaintRegion.GetBounds();
+ printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+ if (aPaintRegion.IsComplex()) {
+ printf_stderr("Complex region\n");
+ for (auto iter = aPaintRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& rect = iter.Get();
+ printf_stderr(" rect %i, %i, %i, %i\n",
+ rect.x, rect.y, rect.width, rect.height);
+ }
+ }
+ }
+ start = PR_IntervalNow();
+#endif
+
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "PaintThebesUpdate",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ mNewValidRegion = aNewValidRegion;
+ Update(aNewValidRegion, aPaintRegion, aDirtyRegion);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (PR_IntervalNow() - start > 10) {
+ const IntRect bounds = aPaintRegion.GetBounds();
+ printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+#endif
+
+ mLastPaintContentType = GetContentType(&mLastPaintSurfaceMode);
+ mCallback = nullptr;
+ mCallbackData = nullptr;
+}
+
+void PadDrawTargetOutFromRegion(RefPtr<DrawTarget> drawTarget, nsIntRegion &region)
+{
+ struct LockedBits {
+ uint8_t *data;
+ IntSize size;
+ int32_t stride;
+ SurfaceFormat format;
+ static int clamp(int x, int min, int max)
+ {
+ if (x < min)
+ x = min;
+ if (x > max)
+ x = max;
+ return x;
+ }
+
+ static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
+ {
+ if (src + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long src memcpy");
+ }
+ if (src < bitmap) {
+ MOZ_CRASH("GFX: short src memcpy");
+ }
+ if (dst + n > bitmap + stride*height) {
+ MOZ_CRASH("GFX: long dst mempcy");
+ }
+ if (dst < bitmap) {
+ MOZ_CRASH("GFX: short dst mempcy");
+ }
+ }
+
+ static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
+ LockedBits *lb = static_cast<LockedBits*>(closure);
+ uint8_t *bitmap = lb->data;
+ const int bpp = gfx::BytesPerPixel(lb->format);
+ const int stride = lb->stride;
+ const int width = lb->size.width;
+ const int height = lb->size.height;
+
+ if (side == VisitSide::TOP) {
+ if (y1 > 0) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + (y1-1) * stride], &bitmap[x1*bpp + y1 * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ if (y1 < height) {
+ x1 = clamp(x1, 0, width - 1);
+ x2 = clamp(x2, 0, width - 1);
+ ensure_memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp, bitmap, stride, height);
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[x1*bpp + (y1-1) * stride], (x2 - x1) * bpp);
+ }
+ } else if (side == VisitSide::LEFT) {
+ if (x1 > 0) {
+ while (y1 != y2) {
+ memcpy(&bitmap[(x1-1)*bpp + y1 * stride], &bitmap[x1*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ } else if (side == VisitSide::RIGHT) {
+ if (x1 < width) {
+ while (y1 != y2) {
+ memcpy(&bitmap[x1*bpp + y1 * stride], &bitmap[(x1-1)*bpp + y1*stride], bpp);
+ y1++;
+ }
+ }
+ }
+
+ }
+ } lb;
+
+ if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
+ // we can only pad software targets so if we can't lock the bits don't pad
+ region.VisitEdges(lb.visitor, &lb);
+ drawTarget->ReleaseBits(lb.data);
+ }
+}
+
+void
+ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
+{
+ // We locked the back buffer, and flipped so we now need to unlock the front
+ if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
+ aTile.mFrontBuffer->Unlock();
+ aTile.mFrontBuffer->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
+ aTile.mFrontBufferOnWhite->Unlock();
+ aTile.mFrontBufferOnWhite->SyncWithObject(mCompositableClient.GetForwarder()->GetSyncObject());
+ }
+ if (aTile.mBackBuffer && aTile.mBackBuffer->IsLocked()) {
+ aTile.mBackBuffer->Unlock();
+ }
+ if (aTile.mBackBufferOnWhite && aTile.mBackBufferOnWhite->IsLocked()) {
+ aTile.mBackBufferOnWhite->Unlock();
+ }
+}
+
+void ClientMultiTiledLayerBuffer::Update(const nsIntRegion& newValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion)
+{
+ const IntSize scaledTileSize = GetScaledTileSize();
+ const gfx::IntRect newBounds = newValidRegion.GetBounds();
+
+ const TilesPlacement oldTiles = mTiles;
+ const TilesPlacement newTiles(floor_div(newBounds.x, scaledTileSize.width),
+ floor_div(newBounds.y, scaledTileSize.height),
+ floor_div(GetTileStart(newBounds.x, scaledTileSize.width)
+ + newBounds.width, scaledTileSize.width) + 1,
+ floor_div(GetTileStart(newBounds.y, scaledTileSize.height)
+ + newBounds.height, scaledTileSize.height) + 1);
+
+ const size_t oldTileCount = mRetainedTiles.Length();
+ const size_t newTileCount = newTiles.mSize.width * newTiles.mSize.height;
+
+ nsTArray<TileClient> oldRetainedTiles;
+ mRetainedTiles.SwapElements(oldRetainedTiles);
+ mRetainedTiles.SetLength(newTileCount);
+
+ for (size_t oldIndex = 0; oldIndex < oldTileCount; oldIndex++) {
+ const TileIntPoint tilePosition = oldTiles.TilePosition(oldIndex);
+ const size_t newIndex = newTiles.TileIndex(tilePosition);
+ // First, get the already existing tiles to the right place in the new array.
+ // Leave placeholders (default constructor) where there was no tile.
+ if (newTiles.HasTile(tilePosition)) {
+ mRetainedTiles[newIndex] = oldRetainedTiles[oldIndex];
+ } else {
+ // release tiles that we are not going to reuse before allocating new ones
+ // to avoid allocating unnecessarily.
+ oldRetainedTiles[oldIndex].DiscardBuffers();
+ }
+ }
+
+ oldRetainedTiles.Clear();
+
+ if (!aPaintRegion.IsEmpty()) {
+ for (size_t i = 0; i < newTileCount; ++i) {
+ const TileIntPoint tilePosition = newTiles.TilePosition(i);
+
+ IntPoint tileOffset = GetTileOffset(tilePosition);
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+ tileDrawRegion.AndWith(aPaintRegion);
+
+ if (tileDrawRegion.IsEmpty()) {
+ continue;
+ }
+
+ TileClient& tile = mRetainedTiles[i];
+ if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
+ gfxCriticalError() << "ValidateTile failed";
+ }
+ }
+
+ if (mMoz2DTiles.size() > 0) {
+ gfx::TileSet tileset;
+ for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
+ mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
+ }
+ tileset.mTiles = &mMoz2DTiles[0];
+ tileset.mTileCount = mMoz2DTiles.size();
+ RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+ if (!drawTarget || !drawTarget->IsValid()) {
+ gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
+ return;
+ }
+ drawTarget->SetTransform(Matrix());
+
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
+ MOZ_ASSERT(ctx); // already checked the draw target above
+ ctx->SetMatrix(
+ ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
+
+ mCallback(&mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
+ DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+ mMoz2DTiles.clear();
+ // Reset:
+ mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
+ std::numeric_limits<int32_t>::max());
+ }
+
+ bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
+
+ for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
+ TileClient& tile = mRetainedTiles[i];
+
+ // Only worry about padding when not doing low-res because it simplifies
+ // the math and the artifacts won't be noticable
+ // Edge padding prevents sampling artifacts when compositing.
+ if (edgePaddingEnabled && mResolution == 1 &&
+ tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
+
+ const TileIntPoint tilePosition = newTiles.TilePosition(i);
+ IntPoint tileOffset = GetTileOffset(tilePosition);
+ // Strictly speakig we want the unscaled rect here, but it doesn't matter
+ // because we only run this code when the resolution is equal to 1.
+ IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
+ GetTileSize().width, GetTileSize().height);
+
+ nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+ tileDrawRegion.AndWith(aPaintRegion);
+
+ nsIntRegion tileValidRegion = mValidRegion;
+ tileValidRegion.OrWith(tileDrawRegion);
+
+ // We only need to pad out if the tile has area that's not valid
+ if (!tileValidRegion.Contains(tileRect)) {
+ tileValidRegion = tileValidRegion.Intersect(tileRect);
+ // translate the region into tile space and pad
+ tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
+ RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
+ PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
+ }
+ }
+ UnlockTile(tile);
+ }
+ }
+
+ mTiles = newTiles;
+ mValidRegion = newValidRegion;
+ mPaintedRegion.OrWith(aPaintRegion);
+}
+
+bool
+ClientMultiTiledLayerBuffer::ValidateTile(TileClient& aTile,
+ const nsIntPoint& aTileOrigin,
+ const nsIntRegion& aDirtyRegion)
+{
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "ValidateTile",
+ js::ProfileEntry::Category::GRAPHICS);
+
+#ifdef GFX_TILEDLAYER_PREF_WARNINGS
+ if (aDirtyRegion.IsComplex()) {
+ printf_stderr("Complex region\n");
+ }
+#endif
+
+ SurfaceMode mode;
+ gfxContentType content = GetContentType(&mode);
+
+ if (!aTile.mAllocator) {
+ aTile.SetTextureAllocator(mManager->GetCompositorBridgeChild()->GetTexturePool(
+ mManager->AsShadowForwarder(),
+ gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
+ TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD));
+ MOZ_ASSERT(aTile.mAllocator);
+ }
+
+ nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
+ offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
+
+ nsIntRegion extraPainted;
+ RefPtr<TextureClient> backBufferOnWhite;
+ RefPtr<TextureClient> backBuffer =
+ aTile.GetBackBuffer(mCompositableClient,
+ offsetScaledDirtyRegion,
+ content, mode,
+ extraPainted,
+ &backBufferOnWhite);
+
+ aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
+ extraPainted.MoveBy(aTileOrigin);
+ extraPainted.And(extraPainted, mNewValidRegion);
+ mPaintedRegion.Or(mPaintedRegion, extraPainted);
+
+ if (!backBuffer) {
+ return false;
+ }
+
+ gfx::Tile moz2DTile;
+ RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
+ RefPtr<DrawTarget> dtOnWhite;
+ if (backBufferOnWhite) {
+ dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
+ moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
+ } else {
+ moz2DTile.mDrawTarget = dt;
+ }
+ moz2DTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+ if (!dt || (backBufferOnWhite && !dtOnWhite)) {
+ aTile.DiscardBuffers();
+ return false;
+ }
+
+ mMoz2DTiles.push_back(moz2DTile);
+ mTilingOrigin.x = std::min(mTilingOrigin.x, moz2DTile.mTileOrigin.x);
+ mTilingOrigin.y = std::min(mTilingOrigin.y, moz2DTile.mTileOrigin.y);
+
+ for (auto iter = aDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ const IntRect& dirtyRect = iter.Get();
+ gfx::Rect drawRect(dirtyRect.x - aTileOrigin.x,
+ dirtyRect.y - aTileOrigin.y,
+ dirtyRect.width,
+ dirtyRect.height);
+ drawRect.Scale(mResolution);
+
+ // Mark the newly updated area as invalid in the front buffer
+ aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect::RoundOut(drawRect));
+
+ if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+ dt->FillRect(drawRect, ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+ dtOnWhite->FillRect(drawRect, ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+ } else if (content == gfxContentType::COLOR_ALPHA) {
+ dt->ClearRect(drawRect);
+ }
+ }
+
+ // The new buffer is now validated, remove the dirty region from it.
+ aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
+
+ aTile.Flip();
+
+ return true;
+}
+
+/**
+ * This function takes the transform stored in aTransformToCompBounds
+ * (which was generated in GetTransformToAncestorsParentLayer), and
+ * modifies it with the ViewTransform from the compositor side so that
+ * it reflects what the compositor is actually rendering. This operation
+ * basically adds in the layer's async transform.
+ * This function then returns the scroll ancestor's composition bounds,
+ * transformed into the painted layer's LayerPixel coordinates, accounting
+ * for the compositor state.
+ */
+static Maybe<LayerRect>
+GetCompositorSideCompositionBounds(const LayerMetricsWrapper& aScrollAncestor,
+ const LayerToParentLayerMatrix4x4& aTransformToCompBounds,
+ const AsyncTransform& aAPZTransform,
+ const LayerRect& aClip)
+{
+ LayerToParentLayerMatrix4x4 transform = aTransformToCompBounds *
+ AsyncTransformComponentMatrix(aAPZTransform);
+
+ return UntransformBy(transform.Inverse(),
+ aScrollAncestor.Metrics().GetCompositionBounds(), aClip);
+}
+
+bool
+ClientMultiTiledLayerBuffer::ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ nsIntRegion& aRegionToPaint,
+ BasicTiledLayerPaintData* aPaintData,
+ bool aIsRepeated)
+{
+ aRegionToPaint = aInvalidRegion;
+
+ // If the composition bounds rect is empty, we can't make any sensible
+ // decision about how to update coherently. In this case, just update
+ // everything in one transaction.
+ if (aPaintData->mCompositionBounds.IsEmpty()) {
+ aPaintData->mPaintFinished = true;
+ return false;
+ }
+
+ // If this is a low precision buffer, we force progressive updates. The
+ // assumption is that the contents is less important, so visual coherency
+ // is lower priority than speed.
+ bool drawingLowPrecision = IsLowPrecision();
+
+ // Find out if we have any non-stale content to update.
+ nsIntRegion staleRegion;
+ staleRegion.And(aInvalidRegion, aOldValidRegion);
+
+ TILING_LOG("TILING %p: Progressive update stale region %s\n", &mPaintedLayer, Stringify(staleRegion).c_str());
+
+ LayerMetricsWrapper scrollAncestor;
+ mPaintedLayer.GetAncestorLayers(&scrollAncestor, nullptr, nullptr);
+
+ // Find out the current view transform to determine which tiles to draw
+ // first, and see if we should just abort this paint. Aborting is usually
+ // caused by there being an incoming, more relevant paint.
+ AsyncTransform viewTransform;
+ MOZ_ASSERT(mSharedFrameMetricsHelper);
+
+ bool abortPaint =
+ mSharedFrameMetricsHelper->UpdateFromCompositorFrameMetrics(
+ scrollAncestor,
+ !staleRegion.Contains(aInvalidRegion),
+ drawingLowPrecision,
+ viewTransform);
+
+ TILING_LOG("TILING %p: Progressive update view transform %s zoom %f abort %d\n",
+ &mPaintedLayer, ToString(viewTransform.mTranslation).c_str(), viewTransform.mScale.scale, abortPaint);
+
+ if (abortPaint) {
+ // We ignore if front-end wants to abort if this is the first,
+ // non-low-precision paint, as in that situation, we're about to override
+ // front-end's page/viewport metrics.
+ if (!aPaintData->mFirstPaint || drawingLowPrecision) {
+ PROFILER_LABEL("ClientMultiTiledLayerBuffer", "ComputeProgressiveUpdateRegion",
+ js::ProfileEntry::Category::GRAPHICS);
+
+ aRegionToPaint.SetEmpty();
+ return aIsRepeated;
+ }
+ }
+
+ Maybe<LayerRect> transformedCompositionBounds =
+ GetCompositorSideCompositionBounds(scrollAncestor,
+ aPaintData->mTransformToCompBounds,
+ viewTransform,
+ ViewAs<LayerPixel>(Rect(mPaintedLayer.GetLayerBounds())));
+
+ if (!transformedCompositionBounds) {
+ aPaintData->mPaintFinished = true;
+ return false;
+ }
+
+ TILING_LOG("TILING %p: Progressive update transformed compositor bounds %s\n", &mPaintedLayer, Stringify(*transformedCompositionBounds).c_str());
+
+ // Compute a "coherent update rect" that we should paint all at once in a
+ // single transaction. This is to avoid rendering glitches on animated
+ // page content, and when layers change size/shape.
+ // On Fennec uploads are more expensive because we're not using gralloc, so
+ // we use a coherent update rect that is intersected with the screen at the
+ // time of issuing the draw command. This will paint faster but also potentially
+ // make the progressive paint more visible to the user while scrolling.
+ // On B2G uploads are cheaper and we value coherency more, especially outside
+ // the browser, so we always use the entire user-visible area.
+ IntRect coherentUpdateRect(RoundedOut(
+#ifdef MOZ_WIDGET_ANDROID
+ transformedCompositionBounds->Intersect(aPaintData->mCompositionBounds)
+#else
+ *transformedCompositionBounds
+#endif
+ ).ToUnknownRect());
+
+ TILING_LOG("TILING %p: Progressive update final coherency rect %s\n", &mPaintedLayer, Stringify(coherentUpdateRect).c_str());
+
+ aRegionToPaint.And(aInvalidRegion, coherentUpdateRect);
+ aRegionToPaint.Or(aRegionToPaint, staleRegion);
+ bool drawingStale = !aRegionToPaint.IsEmpty();
+ if (!drawingStale) {
+ aRegionToPaint = aInvalidRegion;
+ }
+
+ // Prioritise tiles that are currently visible on the screen.
+ bool paintingVisible = false;
+ if (aRegionToPaint.Intersects(coherentUpdateRect)) {
+ aRegionToPaint.And(aRegionToPaint, coherentUpdateRect);
+ paintingVisible = true;
+ }
+
+ TILING_LOG("TILING %p: Progressive update final paint region %s\n", &mPaintedLayer, Stringify(aRegionToPaint).c_str());
+
+ // Paint area that's visible and overlaps previously valid content to avoid
+ // visible glitches in animated elements, such as gifs.
+ bool paintInSingleTransaction = paintingVisible && (drawingStale || aPaintData->mFirstPaint);
+
+ TILING_LOG("TILING %p: paintingVisible %d drawingStale %d firstPaint %d singleTransaction %d\n",
+ &mPaintedLayer, paintingVisible, drawingStale, aPaintData->mFirstPaint, paintInSingleTransaction);
+
+ // The following code decides what order to draw tiles in, based on the
+ // current scroll direction of the primary scrollable layer.
+ NS_ASSERTION(!aRegionToPaint.IsEmpty(), "Unexpectedly empty paint region!");
+ IntRect paintBounds = aRegionToPaint.GetBounds();
+
+ int startX, incX, startY, incY;
+ gfx::IntSize scaledTileSize = GetScaledTileSize();
+ if (aPaintData->mScrollOffset.x >= aPaintData->mLastScrollOffset.x) {
+ startX = RoundDownToTileEdge(paintBounds.x, scaledTileSize.width);
+ incX = scaledTileSize.width;
+ } else {
+ startX = RoundDownToTileEdge(paintBounds.XMost() - 1, scaledTileSize.width);
+ incX = -scaledTileSize.width;
+ }
+
+ if (aPaintData->mScrollOffset.y >= aPaintData->mLastScrollOffset.y) {
+ startY = RoundDownToTileEdge(paintBounds.y, scaledTileSize.height);
+ incY = scaledTileSize.height;
+ } else {
+ startY = RoundDownToTileEdge(paintBounds.YMost() - 1, scaledTileSize.height);
+ incY = -scaledTileSize.height;
+ }
+
+ // Find a tile to draw.
+ IntRect tileBounds(startX, startY, scaledTileSize.width, scaledTileSize.height);
+ int32_t scrollDiffX = aPaintData->mScrollOffset.x - aPaintData->mLastScrollOffset.x;
+ int32_t scrollDiffY = aPaintData->mScrollOffset.y - aPaintData->mLastScrollOffset.y;
+ // This loop will always terminate, as there is at least one tile area
+ // along the first/last row/column intersecting with regionToPaint, or its
+ // bounds would have been smaller.
+ while (true) {
+ aRegionToPaint.And(aInvalidRegion, tileBounds);
+ if (!aRegionToPaint.IsEmpty()) {
+ if (mResolution != 1) {
+ // Paint the entire tile for low-res. This is aimed to fixing low-res resampling
+ // and to avoid doing costly region accurate painting for a small area.
+ aRegionToPaint = tileBounds;
+ }
+ break;
+ }
+ if (Abs(scrollDiffY) >= Abs(scrollDiffX)) {
+ tileBounds.x += incX;
+ } else {
+ tileBounds.y += incY;
+ }
+ }
+
+ if (!aRegionToPaint.Contains(aInvalidRegion)) {
+ // The region needed to paint is larger then our progressive chunk size
+ // therefore update what we want to paint and ask for a new paint transaction.
+
+ // If we need to draw more than one tile to maintain coherency, make
+ // sure it happens in the same transaction by requesting this work be
+ // repeated immediately.
+ // If this is unnecessary, the remaining work will be done tile-by-tile in
+ // subsequent transactions. The caller code is responsible for scheduling
+ // the subsequent transactions as long as we don't set the mPaintFinished
+ // flag to true.
+ return (!drawingLowPrecision && paintInSingleTransaction);
+ }
+
+ // We're not repeating painting and we've not requested a repeat transaction,
+ // so the paint is finished. If there's still a separate low precision
+ // paint to do, it will get marked as unfinished later.
+ aPaintData->mPaintFinished = true;
+ return false;
+}
+
+bool
+ClientMultiTiledLayerBuffer::ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData)
+{
+ TILING_LOG("TILING %p: Progressive update valid region %s\n", &mPaintedLayer, Stringify(aValidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive update invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
+ TILING_LOG("TILING %p: Progressive update old valid region %s\n", &mPaintedLayer, Stringify(aOldValidRegion).c_str());
+
+ bool repeat = false;
+ bool isBufferChanged = false;
+ do {
+ // Compute the region that should be updated. Repeat as many times as
+ // is required.
+ nsIntRegion regionToPaint;
+ repeat = ComputeProgressiveUpdateRegion(aInvalidRegion,
+ aOldValidRegion,
+ regionToPaint,
+ aPaintData,
+ repeat);
+
+ TILING_LOG("TILING %p: Progressive update computed paint region %s repeat %d\n", &mPaintedLayer, Stringify(regionToPaint).c_str(), repeat);
+
+ // There's no further work to be done.
+ if (regionToPaint.IsEmpty()) {
+ break;
+ }
+
+ isBufferChanged = true;
+
+ // Keep track of what we're about to refresh.
+ aValidRegion.Or(aValidRegion, regionToPaint);
+
+ // aValidRegion may have been altered by InvalidateRegion, but we still
+ // want to display stale content until it gets progressively updated.
+ // Create a region that includes stale content.
+ nsIntRegion validOrStale;
+ validOrStale.Or(aValidRegion, aOldValidRegion);
+
+ // Paint the computed region and subtract it from the invalid region.
+ PaintThebes(validOrStale, regionToPaint, aInvalidRegion,
+ aCallback, aCallbackData, true);
+ aInvalidRegion.Sub(aInvalidRegion, regionToPaint);
+ } while (repeat);
+
+ TILING_LOG("TILING %p: Progressive update final valid region %s buffer changed %d\n", &mPaintedLayer, Stringify(aValidRegion).c_str(), isBufferChanged);
+ TILING_LOG("TILING %p: Progressive update final invalid region %s\n", &mPaintedLayer, Stringify(aInvalidRegion).c_str());
+
+ // Return false if nothing has been drawn, or give what has been drawn
+ // to the shadow layer to upload.
+ return isBufferChanged;
+}
+
+void
+TiledContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%sTiledContentClient (0x%p)", mName, this).get();
+
+ if (profiler_feature_active("displaylistdump")) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ Dump(aStream, pfx.get(), false);
+ }
+}
+
+void
+TiledContentClient::Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress)
+{
+ GetTiledBuffer()->Dump(aStream, aPrefix, aDumpHtml, aCompress);
+}
+
+void
+BasicTiledLayerPaintData::ResetPaintData()
+{
+ mLowPrecisionPaintCount = 0;
+ mPaintFinished = false;
+ mHasTransformAnimation = false;
+ mCompositionBounds.SetEmpty();
+ mCriticalDisplayPort = Nothing();
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/client/TiledContentClient.h b/gfx/layers/client/TiledContentClient.h
new file mode 100644
index 000000000..cf55450f8
--- /dev/null
+++ b/gfx/layers/client/TiledContentClient.h
@@ -0,0 +1,545 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_TILEDCONTENTCLIENT_H
+#define MOZILLA_GFX_TILEDCONTENTCLIENT_H
+
+#include <stddef.h> // for size_t
+#include <stdint.h> // for uint16_t
+#include <algorithm> // for swap
+#include <limits>
+#include "Layers.h" // for LayerManager, etc
+#include "TiledLayerBuffer.h" // for TiledLayerBuffer
+#include "Units.h" // for CSSPoint
+#include "gfxTypes.h"
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/ipc/SharedMemory.h" // for SharedMemory
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
+#include "mozilla/layers/CompositableClient.h" // for CompositableClient
+#include "mozilla/layers/CompositorTypes.h" // for TextureInfo, etc
+#include "mozilla/layers/LayersMessages.h" // for TileDescriptor
+#include "mozilla/layers/LayersTypes.h" // for TextureDumpMode
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureClientPool.h"
+#include "ClientLayerManager.h"
+#include "mozilla/mozalloc.h" // for operator delete
+#include "nsISupportsImpl.h" // for MOZ_COUNT_DTOR
+#include "nsPoint.h" // for nsIntPoint
+#include "nsRect.h" // for mozilla::gfx::IntRect
+#include "nsRegion.h" // for nsIntRegion
+#include "nsTArray.h" // for nsTArray, nsTArray_Impl, etc
+#include "nsExpirationTracker.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class ClientTiledPaintedLayer;
+class ClientLayerManager;
+
+/**
+ * Represent a single tile in tiled buffer. The buffer keeps tiles,
+ * each tile keeps a reference to a texture client and a read-lock. This
+ * read-lock is used to help implement a copy-on-write mechanism. The tile
+ * should be locked before being sent to the compositor. The compositor should
+ * unlock the read-lock as soon as it has finished with the buffer in the
+ * TextureHost to prevent more textures being created than is necessary.
+ * Ideal place to store per tile debug information.
+ */
+struct TileClient
+{
+ // Placeholder
+ TileClient();
+ ~TileClient();
+
+ TileClient(const TileClient& o);
+
+ TileClient& operator=(const TileClient& o);
+
+ bool operator== (const TileClient& o) const
+ {
+ return mFrontBuffer == o.mFrontBuffer;
+ }
+
+ bool operator!= (const TileClient& o) const
+ {
+ return mFrontBuffer != o.mFrontBuffer;
+ }
+
+ void SetTextureAllocator(TextureClientAllocator* aAllocator)
+ {
+ mAllocator = aAllocator;
+ }
+
+ bool IsPlaceholderTile() const
+ {
+ return mBackBuffer == nullptr && mFrontBuffer == nullptr;
+ }
+
+ void DiscardBuffers()
+ {
+ DiscardFrontBuffer();
+ DiscardBackBuffer();
+ }
+
+ nsExpirationState *GetExpirationState() { return &mExpirationState; }
+
+ TileDescriptor GetTileDescriptor();
+
+ /**
+ * For debugging.
+ */
+ void Dump(std::stringstream& aStream);
+
+ /**
+ * Swaps the front and back buffers.
+ */
+ void Flip();
+
+ void DumpTexture(std::stringstream& aStream, TextureDumpMode aCompress) {
+ // TODO We should combine the OnWhite/OnBlack here an just output a single image.
+ CompositableClient::DumpTextureClient(aStream, mFrontBuffer, aCompress);
+ }
+
+ /**
+ * Returns an unlocked TextureClient that can be used for writing new
+ * data to the tile. This may flip the front-buffer to the back-buffer if
+ * the front-buffer is still locked by the host, or does not have an
+ * internal buffer (and so will always be locked).
+ *
+ * If getting the back buffer required copying pixels from the front buffer
+ * then the copied region is stored in aAddPaintedRegion so the host side
+ * knows to upload it.
+ *
+ * If nullptr is returned, aTextureClientOnWhite is undefined.
+ */
+ TextureClient* GetBackBuffer(CompositableClient&,
+ const nsIntRegion& aDirtyRegion,
+ gfxContentType aContent, SurfaceMode aMode,
+ nsIntRegion& aAddPaintedRegion,
+ RefPtr<TextureClient>* aTextureClientOnWhite);
+
+ void DiscardFrontBuffer();
+
+ void DiscardBackBuffer();
+
+ /* We wrap the back buffer in a class that disallows assignment
+ * so that we can track when ever it changes so that we can update
+ * the expiry tracker for expiring the back buffers */
+ class PrivateProtector {
+ public:
+ void Set(TileClient * container, RefPtr<TextureClient>);
+ void Set(TileClient * container, TextureClient*);
+ // Implicitly convert to TextureClient* because we can't chain
+ // implicit conversion that would happen on RefPtr<TextureClient>
+ operator TextureClient*() const { return mBuffer; }
+ RefPtr<TextureClient> operator ->() { return mBuffer; }
+ private:
+ PrivateProtector& operator=(const PrivateProtector &);
+ RefPtr<TextureClient> mBuffer;
+ } mBackBuffer;
+ RefPtr<TextureClient> mBackBufferOnWhite;
+ RefPtr<TextureClient> mFrontBuffer;
+ RefPtr<TextureClient> mFrontBufferOnWhite;
+ RefPtr<TextureClientAllocator> mAllocator;
+ gfx::IntRect mUpdateRect;
+ bool mWasPlaceholder;
+#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
+ TimeStamp mLastUpdate;
+#endif
+ nsIntRegion mInvalidFront;
+ nsIntRegion mInvalidBack;
+ nsExpirationState mExpirationState;
+private:
+ // Copies dirty pixels from the front buffer into the back buffer,
+ // and records the copied region in aAddPaintedRegion.
+ void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
+ nsIntRegion& aAddPaintedRegion);
+};
+
+/**
+ * This struct stores all the data necessary to perform a paint so that it
+ * doesn't need to be recalculated on every repeated transaction.
+ */
+struct BasicTiledLayerPaintData {
+ /*
+ * The scroll offset of the content from the nearest ancestor layer that
+ * represents scrollable content with a display port set.
+ */
+ ParentLayerPoint mScrollOffset;
+
+ /*
+ * The scroll offset of the content from the nearest ancestor layer that
+ * represents scrollable content with a display port set, for the last
+ * layer update transaction.
+ */
+ ParentLayerPoint mLastScrollOffset;
+
+ /*
+ * The transform matrix to go from this layer's Layer units to
+ * the scroll ancestor's ParentLayer units. The "scroll ancestor" is
+ * the closest ancestor layer which scrolls, and is used to obtain
+ * the composition bounds that are relevant for this layer.
+ */
+ LayerToParentLayerMatrix4x4 mTransformToCompBounds;
+
+ /*
+ * The critical displayport of the content from the nearest ancestor layer
+ * that represents scrollable content with a display port set. isNothing()
+ * if a critical displayport is not set.
+ */
+ Maybe<LayerIntRect> mCriticalDisplayPort;
+
+ /*
+ * The render resolution of the document that the content this layer
+ * represents is in.
+ */
+ CSSToParentLayerScale2D mResolution;
+
+ /*
+ * The composition bounds of the layer, in Layer coordinates. This is
+ * used to make sure that tiled updates to regions that are visible to the
+ * user are grouped coherently.
+ */
+ LayerRect mCompositionBounds;
+
+ /*
+ * Low precision updates are always executed a tile at a time in repeated
+ * transactions. This counter is set to 1 on the first transaction of a low
+ * precision update, and incremented for each subsequent transaction.
+ */
+ uint16_t mLowPrecisionPaintCount;
+
+ /*
+ * Whether this is the first time this layer is painting
+ */
+ bool mFirstPaint : 1;
+
+ /*
+ * Whether there is further work to complete this paint. This is used to
+ * determine whether or not to repeat the transaction when painting
+ * progressively.
+ */
+ bool mPaintFinished : 1;
+
+ /*
+ * Whether or not there is an async transform animation active
+ */
+ bool mHasTransformAnimation : 1;
+
+ /*
+ * Initializes/clears data to prepare for paint action.
+ */
+ void ResetPaintData();
+};
+
+class SharedFrameMetricsHelper
+{
+public:
+ SharedFrameMetricsHelper();
+ ~SharedFrameMetricsHelper();
+
+ /**
+ * This is called by the BasicTileLayer to determine if it is still interested
+ * in the update of this display-port to continue. We can return true here
+ * to abort the current update and continue with any subsequent ones. This
+ * is useful for slow-to-render pages when the display-port starts lagging
+ * behind enough that continuing to draw it is wasted effort.
+ */
+ bool UpdateFromCompositorFrameMetrics(const LayerMetricsWrapper& aLayer,
+ bool aHasPendingNewThebesContent,
+ bool aLowPrecision,
+ AsyncTransform& aViewTransform);
+
+ /**
+ * Determines if the compositor's upcoming composition bounds has fallen
+ * outside of the contents display port. If it has then the compositor
+ * will start to checker board. Checker boarding is when the compositor
+ * tries to composite a tile and it is not available. Historically
+ * a tile with a checker board pattern was used. Now a blank tile is used.
+ */
+ bool AboutToCheckerboard(const FrameMetrics& aContentMetrics,
+ const FrameMetrics& aCompositorMetrics);
+private:
+ bool mLastProgressiveUpdateWasLowPrecision;
+ bool mProgressiveUpdateWasInDanger;
+};
+
+/**
+ * Provide an instance of TiledLayerBuffer backed by drawable TextureClients.
+ * This buffer provides an implementation of ValidateTile using a
+ * thebes callback and can support painting using a single paint buffer.
+ * Whether a single paint buffer is used is controlled by
+ * gfxPrefs::PerTileDrawing().
+ */
+class ClientTiledLayerBuffer
+{
+public:
+ ClientTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient)
+ : mPaintedLayer(aPaintedLayer)
+ , mCompositableClient(aCompositableClient)
+ , mLastPaintContentType(gfxContentType::COLOR)
+ , mLastPaintSurfaceMode(SurfaceMode::SURFACE_OPAQUE)
+ , mWasLastPaintProgressive(false)
+ {}
+
+ virtual void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) = 0;
+
+ virtual bool SupportsProgressiveUpdate() = 0;
+ virtual bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) = 0;
+ virtual void ResetPaintedAndValidState() = 0;
+
+ virtual const nsIntRegion& GetValidRegion() = 0;
+
+ virtual bool IsLowPrecision() const = 0;
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress) {}
+
+ const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
+ void SetFrameResolution(const CSSToParentLayerScale2D& aResolution) { mFrameResolution = aResolution; }
+
+ bool HasFormatChanged() const;
+
+protected:
+ void UnlockTile(TileClient& aTile);
+ gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
+
+ ClientTiledPaintedLayer& mPaintedLayer;
+ CompositableClient& mCompositableClient;
+
+ gfxContentType mLastPaintContentType;
+ SurfaceMode mLastPaintSurfaceMode;
+ CSSToParentLayerScale2D mFrameResolution;
+
+ bool mWasLastPaintProgressive;
+};
+
+class ClientMultiTiledLayerBuffer
+ : public TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>
+ , public ClientTiledLayerBuffer
+{
+ friend class TiledLayerBuffer<ClientMultiTiledLayerBuffer, TileClient>;
+public:
+ ClientMultiTiledLayerBuffer(ClientTiledPaintedLayer& aPaintedLayer,
+ CompositableClient& aCompositableClient,
+ ClientLayerManager* aManager,
+ SharedFrameMetricsHelper* aHelper);
+
+ void PaintThebes(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ bool aIsProgressive = false) override;
+
+ virtual bool SupportsProgressiveUpdate() override { return true; }
+ /**
+ * Performs a progressive update of a given tiled buffer.
+ * See ComputeProgressiveUpdateRegion below for parameter documentation.
+ */
+ bool ProgressiveUpdate(nsIntRegion& aValidRegion,
+ nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ BasicTiledLayerPaintData* aPaintData,
+ LayerManager::DrawPaintedLayerCallback aCallback,
+ void* aCallbackData) override;
+
+ void ResetPaintedAndValidState() override {
+ mPaintedRegion.SetEmpty();
+ mValidRegion.SetEmpty();
+ mTiles.mSize.width = 0;
+ mTiles.mSize.height = 0;
+ DiscardBuffers();
+ mRetainedTiles.Clear();
+ }
+
+
+ const nsIntRegion& GetValidRegion() override {
+ return TiledLayerBuffer::GetValidRegion();
+ }
+
+ bool IsLowPrecision() const override {
+ return TiledLayerBuffer::IsLowPrecision();
+ }
+
+ void Dump(std::stringstream& aStream,
+ const char* aPrefix,
+ bool aDumpHtml,
+ TextureDumpMode aCompress) override {
+ TiledLayerBuffer::Dump(aStream, aPrefix, aDumpHtml, aCompress);
+ }
+
+ void ReadLock();
+
+ void Release();
+
+ void DiscardBuffers();
+
+ SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
+
+ void SetResolution(float aResolution) {
+ if (mResolution == aResolution) {
+ return;
+ }
+
+ Update(nsIntRegion(), nsIntRegion(), nsIntRegion());
+ mResolution = aResolution;
+ }
+
+protected:
+ bool ValidateTile(TileClient& aTile,
+ const nsIntPoint& aTileRect,
+ const nsIntRegion& dirtyRect);
+
+ void Update(const nsIntRegion& aNewValidRegion,
+ const nsIntRegion& aPaintRegion,
+ const nsIntRegion& aDirtyRegion);
+
+ TileClient GetPlaceholderTile() const { return TileClient(); }
+
+private:
+ RefPtr<ClientLayerManager> mManager;
+ LayerManager::DrawPaintedLayerCallback mCallback;
+ void* mCallbackData;
+
+ // The region that will be made valid during Update(). Once Update() is
+ // completed then this is identical to mValidRegion.
+ nsIntRegion mNewValidRegion;
+
+ SharedFrameMetricsHelper* mSharedFrameMetricsHelper;
+ // When using Moz2D's CreateTiledDrawTarget we maintain a list of gfx::Tiles
+ std::vector<gfx::Tile> mMoz2DTiles;
+ /**
+ * While we're adding tiles, this is used to keep track of the position of
+ * the top-left of the top-left-most tile. When we come to wrap the tiles in
+ * TiledDrawTarget we subtract the value of this member from each tile's
+ * offset so that all the tiles have a positive offset, then add a
+ * translation to the TiledDrawTarget to compensate. This is important so
+ * that the mRect of the TiledDrawTarget is always at a positive x/y
+ * position, otherwise its GetSize() methods will be broken.
+ */
+ gfx::IntPoint mTilingOrigin;
+ /**
+ * Calculates the region to update in a single progressive update transaction.
+ * This employs some heuristics to update the most 'sensible' region to
+ * update at this point in time, and how large an update should be performed
+ * at once to maintain visual coherency.
+ *
+ * aInvalidRegion is the current invalid region.
+ * aOldValidRegion is the valid region of mTiledBuffer at the beginning of the
+ * current transaction.
+ * aRegionToPaint will be filled with the region to update. This may be empty,
+ * which indicates that there is no more work to do.
+ * aIsRepeated should be true if this function has already been called during
+ * this transaction.
+ *
+ * Returns true if it should be called again, false otherwise. In the case
+ * that aRegionToPaint is empty, this will return aIsRepeated for convenience.
+ */
+ bool ComputeProgressiveUpdateRegion(const nsIntRegion& aInvalidRegion,
+ const nsIntRegion& aOldValidRegion,
+ nsIntRegion& aRegionToPaint,
+ BasicTiledLayerPaintData* aPaintData,
+ bool aIsRepeated);
+};
+
+class TiledContentClient : public CompositableClient
+{
+public:
+ TiledContentClient(ClientLayerManager* aManager,
+ const char* aName = "")
+ : CompositableClient(aManager->AsShadowForwarder())
+ , mName(aName)
+ {}
+
+protected:
+ ~TiledContentClient()
+ {}
+
+public:
+ virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
+
+ virtual void Dump(std::stringstream& aStream,
+ const char* aPrefix="",
+ bool aDumpHtml=false,
+ TextureDumpMode aCompress=TextureDumpMode::Compress) override;
+
+ virtual TextureInfo GetTextureInfo() const override
+ {
+ return TextureInfo(CompositableType::CONTENT_TILED);
+ }
+
+
+ virtual ClientTiledLayerBuffer* GetTiledBuffer() = 0;
+ virtual ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() = 0;
+
+ enum TiledBufferType {
+ TILED_BUFFER,
+ LOW_PRECISION_TILED_BUFFER
+ };
+ virtual void UpdatedBuffer(TiledBufferType aType) = 0;
+
+private:
+ const char* mName;
+};
+
+/**
+ * An implementation of TiledContentClient that supports
+ * multiple tiles and a low precision buffer.
+ */
+class MultiTiledContentClient : public TiledContentClient
+{
+public:
+ MultiTiledContentClient(ClientTiledPaintedLayer& aPaintedLayer,
+ ClientLayerManager* aManager);
+
+protected:
+ ~MultiTiledContentClient()
+ {
+ MOZ_COUNT_DTOR(MultiTiledContentClient);
+
+ mTiledBuffer.DiscardBuffers();
+ mLowPrecisionTiledBuffer.DiscardBuffers();
+ }
+
+public:
+ void ClearCachedResources() override;
+ void UpdatedBuffer(TiledBufferType aType) override;
+
+ ClientTiledLayerBuffer* GetTiledBuffer() override { return &mTiledBuffer; }
+ ClientTiledLayerBuffer* GetLowPrecisionTiledBuffer() override {
+ if (mHasLowPrecision) {
+ return &mLowPrecisionTiledBuffer;
+ }
+ return nullptr;
+ }
+
+private:
+ SharedFrameMetricsHelper mSharedFrameMetricsHelper;
+ ClientMultiTiledLayerBuffer mTiledBuffer;
+ ClientMultiTiledLayerBuffer mLowPrecisionTiledBuffer;
+ bool mHasLowPrecision;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif