Bug 1200595 - Buffer TextureData implementation. r=mattwoodrow
☠☠ backed out by 9783264529d3 ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Thu, 15 Oct 2015 17:53:33 +0200
changeset 308069 8cc12f12f3d1916c87d14871e86b1c5ad17ead43
parent 308068 bb84403701b7f86e22c75ebe6980e12424d07eca
child 308070 0dc93424546c50a7b74b2ae78d0cb4b3919ea58c
push idunknown
push userunknown
push dateunknown
reviewersmattwoodrow
bugs1200595
milestone45.0a1
Bug 1200595 - Buffer TextureData implementation. r=mattwoodrow
gfx/layers/AsyncCanvasRenderer.cpp
gfx/layers/BufferTexture.cpp
gfx/layers/BufferTexture.h
gfx/layers/ImageDataSerializer.h
gfx/layers/YCbCrImageDataSerializer.cpp
gfx/layers/client/CanvasClient.cpp
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/ImageClient.cpp
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
gfx/layers/ipc/SharedPlanarYCbCrImage.h
gfx/layers/ipc/SharedRGBImage.cpp
gfx/layers/ipc/SharedRGBImage.h
gfx/layers/moz.build
gfx/layers/opengl/GrallocTextureClient.cpp
gfx/layers/opengl/GrallocTextureClient.h
gfx/tests/gtest/TestTextures.cpp
--- a/gfx/layers/AsyncCanvasRenderer.cpp
+++ b/gfx/layers/AsyncCanvasRenderer.cpp
@@ -6,16 +6,17 @@
 
 #include "AsyncCanvasRenderer.h"
 
 #include "gfxUtils.h"
 #include "GLContext.h"
 #include "GLReadTexImageHelper.h"
 #include "GLScreenBuffer.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/layers/BufferTexture.h"
 #include "mozilla/layers/CanvasClient.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
@@ -142,44 +143,48 @@ AsyncCanvasRenderer::GetActiveThread()
   nsCOMPtr<nsIThread> result = mActiveThread;
   return result.forget();
 }
 
 void
 AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient)
 {
   MutexAutoLock lock(mMutex);
-  RefPtr<BufferTextureClient> buffer = static_cast<BufferTextureClient*>(aTextureClient);
-  if (!buffer->Lock(layers::OpenMode::OPEN_READ)) {
+  TextureClientAutoLock texLock(aTextureClient, layers::OpenMode::OPEN_READ);
+  if (texLock.Succeeded()) {
     return;
   }
 
   const gfx::IntSize& size = aTextureClient->GetSize();
   // This buffer would be used later for content rendering. So we choose
   // B8G8R8A8 format here.
   const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
   // Avoid to create buffer every time.
   if (!mSurfaceForBasic ||
       size != mSurfaceForBasic->GetSize() ||
       format != mSurfaceForBasic->GetFormat())
   {
     uint32_t stride = gfx::GetAlignedStride<8>(size.width * BytesPerPixel(format));
     mSurfaceForBasic = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
   }
 
-  const uint8_t* lockedBytes = buffer->GetLockedData();
+  MappedTextureData mapped;
+  if (!aTextureClient->BorrowMappedData(mapped)) {
+    return;
+  }
+
+  const uint8_t* lockedBytes = mapped.data;
   gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
                                         gfx::DataSourceSurface::MapType::WRITE);
   if (!map.IsMapped()) {
-    buffer->Unlock();
     return;
   }
 
+  MOZ_ASSERT(map.GetStride() == mapped.stride);
   memcpy(map.GetData(), lockedBytes, map.GetStride() * mSurfaceForBasic->GetSize().height);
-  buffer->Unlock();
 
   if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
       mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
     gl::SwapRAndBComponents(mSurfaceForBasic);
   }
 }
 
 already_AddRefed<gfx::DataSourceSurface>
new file mode 100644
--- /dev/null
+++ b/gfx/layers/BufferTexture.cpp
@@ -0,0 +1,449 @@
+/* -*- 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 "BufferTexture.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/fallible.h"
+
+namespace mozilla {
+namespace layers {
+
+class MemoryTextureData : public BufferTextureData
+{
+public:
+  static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                   gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
+                                   TextureAllocationFlags aAllocFlags,
+                                   ISurfaceAllocator* aAllocator);
+
+  virtual TextureData*
+  CreateSimilar(ISurfaceAllocator* aAllocator,
+                TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+  virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+  virtual void Deallocate(ISurfaceAllocator*) override;
+
+  MemoryTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                    gfx::BackendType aMoz2DBackend,
+                    uint8_t* aBuffer, size_t aBufferSize)
+  : BufferTextureData(aSize, aFormat, aMoz2DBackend)
+  , mBuffer(aBuffer)
+  , mBufferSize(aBufferSize)
+  {
+    MOZ_ASSERT(aBuffer);
+    MOZ_ASSERT(aBufferSize);
+  }
+
+  virtual uint8_t* GetBuffer() override { return mBuffer; }
+
+  virtual size_t GetBufferSize() override { return mBufferSize; }
+
+protected:
+  uint8_t* mBuffer;
+  size_t mBufferSize;
+};
+
+class ShmemTextureData : public BufferTextureData
+{
+public:
+  static ShmemTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                  gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
+                                  TextureAllocationFlags aAllocFlags,
+                                  ISurfaceAllocator* aAllocator);
+
+  virtual TextureData*
+  CreateSimilar(ISurfaceAllocator* aAllocator,
+                TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
+
+  virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+  virtual void Deallocate(ISurfaceAllocator* aAllocator) override;
+
+  ShmemTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                   gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
+  : BufferTextureData(aSize, aFormat, aMoz2DBackend)
+  , mShmem(aShmem)
+  {
+    MOZ_ASSERT(mShmem.Size<uint8_t>());
+  }
+
+  virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
+
+  virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
+
+protected:
+  mozilla::ipc::Shmem mShmem;
+};
+
+BufferTextureData*
+BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                          gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
+                          TextureAllocationFlags aAllocFlags,
+                          ISurfaceAllocator* aAllocator)
+{
+  if (!aAllocator || aAllocator->IsSameProcess()) {
+    return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+  } else {
+    return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+  }
+}
+
+BufferTextureData*
+BufferTextureData::CreateWithBufferSize(ISurfaceAllocator* aAllocator,
+                                        gfx::SurfaceFormat aFormat,
+                                        size_t aSize,
+                                        TextureFlags aTextureFlags)
+{
+  if (aSize == 0) {
+    return nullptr;
+  }
+
+  BufferTextureData* data;
+  if (!aAllocator || aAllocator->IsSameProcess()) {
+    uint8_t* buffer = new (fallible) uint8_t[aSize];
+    if (!buffer) {
+      return nullptr;
+    }
+
+    data = new MemoryTextureData(gfx::IntSize(), aFormat, gfx::BackendType::NONE, buffer, aSize);
+  } else {
+    ipc::Shmem shm;
+    if (!aAllocator->AllocUnsafeShmem(aSize, OptimalShmemType(), &shm)) {
+      return nullptr;
+    }
+
+    data = new ShmemTextureData(gfx::IntSize(), aFormat, gfx::BackendType::NONE, shm);
+  }
+
+  // Initialize the metadata with something, even if it will have to be rewritten
+  // afterwards since we don't know the dimensions of the texture at this point.
+  if (aFormat == gfx::SurfaceFormat::YUV) {
+    YCbCrImageDataSerializer serializer(data->GetBuffer(), data->GetBufferSize());
+    serializer.InitializeBufferInfo(gfx::IntSize(0,0), gfx::IntSize(0,0), StereoMode::MONO);
+  } else {
+    ImageDataSerializer serializer(data->GetBuffer(), data->GetBufferSize());
+    serializer.InitializeBufferInfo(gfx::IntSize(0, 0), aFormat);
+  }
+
+  return data;
+}
+
+BufferTextureData*
+BufferTextureData::CreateForYCbCr(ISurfaceAllocator* aAllocator,
+                                  gfx::IntSize aYSize,
+                                  gfx::IntSize aCbCrSize,
+                                  StereoMode aStereoMode,
+                                  TextureFlags aTextureFlags)
+{
+  size_t bufSize = YCbCrImageDataSerializer::ComputeMinBufferSize(aYSize, aCbCrSize);
+  BufferTextureData* texture = CreateWithBufferSize(aAllocator, gfx::SurfaceFormat::YUV,
+                                                    bufSize, aTextureFlags);
+  if (!texture) {
+    return nullptr;
+  }
+
+  YCbCrImageDataSerializer serializer(texture->GetBuffer(), texture->GetBufferSize());
+  serializer.InitializeBufferInfo(aYSize, aCbCrSize, aStereoMode);
+  texture->mSize = aYSize;
+
+  return texture;
+}
+
+bool
+BufferTextureData::SupportsMoz2D() const
+{
+  switch (mFormat) {
+    case gfx::SurfaceFormat::YUV:
+    case gfx::SurfaceFormat::NV12:
+    case gfx::SurfaceFormat::UNKNOWN:
+      return false;
+    default:
+      return true;
+  }
+}
+
+already_AddRefed<gfx::DrawTarget>
+BufferTextureData::BorrowDrawTarget()
+{
+  if (mDrawTarget) {
+    mDrawTarget->SetTransform(gfx::Matrix());
+    RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+    return dt.forget();
+  }
+
+  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
+  if (!serializer.IsValid()) {
+    return nullptr;
+  }
+
+  mDrawTarget = serializer.GetAsDrawTarget(mMoz2DBackend);
+  if (mDrawTarget) {
+    RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+    return dt.forget();
+  }
+
+  // TODO - should we warn? should we really fallback to cairo? perhaps
+  // at least update mMoz2DBackend...
+  mDrawTarget = serializer.GetAsDrawTarget(gfx::BackendType::CAIRO);
+  if (!mDrawTarget) {
+    gfxCriticalNote << "BorrowDrawTarget failure, original backend " << (int)mMoz2DBackend;
+  }
+
+  RefPtr<gfx::DrawTarget> dt = mDrawTarget;
+  return dt.forget();
+}
+
+bool
+BufferTextureData::BorrowMappedData(MappedTextureData& aData)
+{
+  if (mFormat == gfx::SurfaceFormat::YUV) {
+    return false;
+  }
+
+  ImageDataDeserializer view(GetBuffer(), GetBufferSize());
+  if (!view.IsValid()) {
+    return false;
+  }
+
+  aData.data = view.GetData();
+  aData.size = view.GetSize();
+  aData.stride = view.GetStride();
+  aData.format = mFormat;
+
+  return true;
+}
+
+bool
+BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+  if (mFormat != gfx::SurfaceFormat::YUV) {
+    return false;
+  }
+
+  YCbCrImageDataDeserializer view(GetBuffer(), GetBufferSize());
+  if (!view.IsValid()) {
+    return false;
+  }
+
+  aMap.stereoMode = view.GetStereoMode();
+  aMap.metadata = GetBuffer();
+
+  aMap.y.data = view.GetYData();
+  aMap.y.size = view.GetYSize();
+  aMap.y.stride = view.GetYStride();
+  aMap.y.skip = 0;
+
+  aMap.cb.data = view.GetCbData();
+  aMap.cb.size = view.GetCbCrSize();
+  aMap.cb.stride = view.GetCbCrStride();
+  aMap.cb.skip = 0;
+
+  aMap.cr.data = view.GetCrData();
+  aMap.cr.size = view.GetCbCrSize();
+  aMap.cr.stride = view.GetCbCrStride();
+  aMap.cr.skip = 0;
+
+  return true;
+}
+
+bool
+BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
+
+  RefPtr<gfx::DataSourceSurface> surface = serializer.GetAsSurface();
+
+  if (!surface) {
+    gfxCriticalError() << "Failed to get serializer as surface!";
+    return false;
+  }
+
+  RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
+
+  if (!srcSurf) {
+    gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface.";
+    return false;
+  }
+
+  if (surface->GetSize() != srcSurf->GetSize() || surface->GetFormat() != srcSurf->GetFormat()) {
+    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
+    return false;
+  }
+
+  gfx::DataSourceSurface::MappedSurface sourceMap;
+  gfx::DataSourceSurface::MappedSurface destMap;
+  if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
+    gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
+    return false;
+  }
+
+  if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
+    srcSurf->Unmap();
+    gfxCriticalError() << "Failed to map destination surface for UpdateFromSurface.";
+    return false;
+  }
+
+
+  for (int y = 0; y < srcSurf->GetSize().height; y++) {
+    memcpy(destMap.mData + destMap.mStride * y,
+           sourceMap.mData + sourceMap.mStride * y,
+           srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
+  }
+
+  srcSurf->Unmap();
+  surface->Unmap();
+
+  return true;
+}
+
+bool
+MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+  MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
+  if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
+    return false;
+  }
+
+  aOutDescriptor = SurfaceDescriptorMemory(reinterpret_cast<uintptr_t>(mBuffer),
+                                           GetFormat());
+  return true;
+}
+
+static bool InitBuffer(uint8_t* buf, size_t bufSize,
+                       gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                       TextureAllocationFlags aAllocFlags)
+{
+  if (!buf) {
+    gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize << " bytes";
+    return false;
+  }
+
+  if (aAllocFlags & ALLOC_CLEAR_BUFFER) {
+    memset(buf, 0, bufSize);
+  }
+  if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
+    memset(buf, 0xFF, bufSize);
+  }
+
+  ImageDataSerializer serializer(buf, bufSize);
+  serializer.InitializeBufferInfo(aSize, aFormat);
+  return true;
+}
+
+MemoryTextureData*
+MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                          gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
+                          TextureAllocationFlags aAllocFlags,
+                          ISurfaceAllocator*)
+{
+  if (aSize.width <= 0 || aSize.height <= 0) {
+    gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+    return nullptr;
+  }
+
+  uint32_t bufSize = ImageDataSerializer::ComputeMinBufferSize(aSize, aFormat);
+  if (!bufSize) {
+    return nullptr;
+  }
+
+  uint8_t* buf = new (fallible) uint8_t[bufSize];
+
+  if (InitBuffer(buf, bufSize, aSize, aFormat, aAllocFlags)) {
+    GfxMemoryImageReporter::DidAlloc(buf);
+    return new MemoryTextureData(aSize, aFormat, aMoz2DBackend, buf, bufSize);
+  }
+
+  return nullptr;
+}
+
+void
+MemoryTextureData::Deallocate(ISurfaceAllocator*)
+{
+  MOZ_ASSERT(mBuffer);
+  GfxMemoryImageReporter::WillFree(mBuffer);
+  delete [] mBuffer;
+  mBuffer = nullptr;
+}
+
+TextureData*
+MemoryTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
+                                 TextureFlags aFlags,
+                                 TextureAllocationFlags aAllocFlags) const
+{
+  return MemoryTextureData::Create(mSize, mFormat, mMoz2DBackend,
+                                   aFlags, aAllocFlags, aAllocator);
+}
+
+bool
+ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+  MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
+  if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
+    return false;
+  }
+
+  aOutDescriptor = SurfaceDescriptorShmem(mShmem, GetFormat());
+
+  return true;
+}
+
+ShmemTextureData*
+ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                         gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
+                         TextureAllocationFlags aAllocFlags,
+                         ISurfaceAllocator* aAllocator)
+{
+  MOZ_ASSERT(aAllocator);
+  if (!aAllocator) {
+    return nullptr;
+  }
+
+  if (aSize.width <= 0 || aSize.height <= 0) {
+    gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+    return nullptr;
+  }
+
+  uint32_t bufSize = ImageDataSerializer::ComputeMinBufferSize(aSize, aFormat);
+  if (!bufSize) {
+    return nullptr;
+  }
+
+  mozilla::ipc::Shmem shm;
+  if (!aAllocator->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
+    return nullptr;
+  }
+
+  uint8_t* buf = shm.get<uint8_t>();
+
+  if (InitBuffer(buf, bufSize, aSize, aFormat, aAllocFlags)) {
+    return new ShmemTextureData(aSize, aFormat, aMoz2DBackend, shm);
+  }
+
+  return nullptr;
+}
+
+TextureData*
+ShmemTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
+                                TextureFlags aFlags,
+                                TextureAllocationFlags aAllocFlags) const
+{
+  return ShmemTextureData::Create(mSize, mFormat, mMoz2DBackend,
+                                  aFlags, aAllocFlags, aAllocator);
+}
+
+void
+ShmemTextureData::Deallocate(ISurfaceAllocator* aAllocator)
+{
+  aAllocator->DeallocShmem(mShmem);
+}
+
+} // namespace
+} // namespace
new file mode 100644
--- /dev/null
+++ b/gfx/layers/BufferTexture.h
@@ -0,0 +1,81 @@
+/* -*- 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_LAYERS_BUFFERETEXTURE
+#define MOZILLA_LAYERS_BUFFERETEXTURE
+
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/YCbCrImageDataSerializer.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace layers {
+
+class BufferTextureData : public TextureData
+{
+public:
+  static BufferTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
+                                   gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
+                                   TextureAllocationFlags aAllocFlags,
+                                   ISurfaceAllocator* aAllocator);
+
+  static BufferTextureData* CreateWithBufferSize(ISurfaceAllocator* aAllocator,
+                                                 gfx::SurfaceFormat aFormat,
+                                                 size_t aSize,
+                                                 TextureFlags aTextureFlags);
+
+  static BufferTextureData* CreateForYCbCr(ISurfaceAllocator* aAllocator,
+                                           gfx::IntSize aYSize,
+                                           gfx::IntSize aCbCrSize,
+                                           StereoMode aStereoMode,
+                                           TextureFlags aTextureFlags);
+
+  virtual bool Lock(OpenMode aMode) override { return true; }
+
+  virtual void Unlock() override {}
+
+  virtual gfx::IntSize GetSize() const override { return mSize; }
+
+  virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
+
+  virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+  virtual bool CanExposeMappedData() const override { return true; }
+
+  virtual bool BorrowMappedData(MappedTextureData& aMap) override;
+
+  virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
+
+  virtual bool SupportsMoz2D() const override;
+
+  virtual bool HasInternalBuffer() const override { return true; }
+
+  // use ClientTexture's default implementation
+  virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+protected:
+  virtual uint8_t* GetBuffer() = 0;
+  virtual size_t GetBufferSize() = 0;
+
+  BufferTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat, gfx::BackendType aMoz2DBackend)
+  : mSize(aSize)
+  , mFormat(aFormat)
+  , mMoz2DBackend(aMoz2DBackend)
+  {}
+
+  RefPtr<gfx::DrawTarget> mDrawTarget;
+  gfx::IntSize mSize;
+  gfx::SurfaceFormat mFormat;
+  gfx::BackendType mMoz2DBackend;
+};
+
+} // namespace
+} // namespace
+
+#endif
--- a/gfx/layers/ImageDataSerializer.h
+++ b/gfx/layers/ImageDataSerializer.h
@@ -34,16 +34,18 @@ public:
   gfx::IntSize GetSize() const;
   gfx::SurfaceFormat GetFormat() const;
   already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
   already_AddRefed<gfx::DrawTarget> GetAsDrawTarget(gfx::BackendType aBackend);
 
   static uint32_t ComputeMinBufferSize(gfx::IntSize aSize,
                                        gfx::SurfaceFormat aFormat);
 
+  size_t GetBufferSize() const { return mDataSize; }
+
 protected:
 
   ImageDataSerializerBase(uint8_t* aData, size_t aDataSize)
     : mData(aData)
     , mDataSize(aDataSize)
     , mIsValid(false)
   {}
 
--- a/gfx/layers/YCbCrImageDataSerializer.cpp
+++ b/gfx/layers/YCbCrImageDataSerializer.cpp
@@ -70,17 +70,17 @@ void YCbCrImageDataDeserializerBase::Val
   if (!info) {
     return;
   }
   size_t requiredSize = ComputeMinBufferSize(
                           IntSize(info->mYWidth, info->mYHeight),
                           info->mYStride,
                           IntSize(info->mCbCrWidth, info->mCbCrHeight),
                           info->mCbCrStride);
-  mIsValid = requiredSize && requiredSize <= mDataSize;
+  mIsValid = requiredSize <= mDataSize;
 
 }
 
 uint8_t* YCbCrImageDataDeserializerBase::GetYData()
 {
   YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize);
   return reinterpret_cast<uint8_t*>(info) + info->mYOffset;
 }
@@ -142,24 +142,25 @@ static size_t ComputeOffset(uint32_t aHe
 // Minimum required shmem size in bytes
 size_t
 YCbCrImageDataDeserializerBase::ComputeMinBufferSize(const gfx::IntSize& aYSize,
                                                      uint32_t aYStride,
                                                      const gfx::IntSize& aCbCrSize,
                                                      uint32_t aCbCrStride)
 {
   MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0);
-  if (aYSize.height <= 0 || aYSize.width <= 0 || aCbCrSize.height <= 0 || aCbCrSize.width <= 0) {
+  if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0) {
     gfxDebug() << "Non-positive YCbCr buffer size request " << aYSize.height << "x" << aYSize.width << ", " << aCbCrSize.height << "x" << aCbCrSize.width;
     return 0;
   }
 
-  if (!gfx::Factory::AllowedSurfaceSize(aYSize) ||
-      aCbCrSize.width > aYSize.width ||
-      aCbCrSize.height > aYSize.height) {
+  if (aYSize != IntSize() &&
+      (!gfx::Factory::AllowedSurfaceSize(aYSize) ||
+       aCbCrSize.width > aYSize.width ||
+       aCbCrSize.height > aYSize.height)) {
     return 0;
   }
 
   return ComputeOffset(aYSize.height, aYStride)
          + 2 * ComputeOffset(aCbCrSize.height, aCbCrStride)
          + MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo));
 }
 
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -8,16 +8,17 @@
 #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/CompositorChild.h" // for CompositorChild
 #include "mozilla/layers/GrallocTextureClient.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient, etc
 #include "mozilla/layers/TextureClientOGL.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
@@ -195,31 +196,31 @@ public:
     , mSize(size)
     , mBackendType(backendType)
     , mBaseTexFlags(baseTexFlags)
     , mLayersBackend(layersBackend)
   {
   }
 
 protected:
-  already_AddRefed<BufferTextureClient> Create(gfx::SurfaceFormat format) {
+  already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
     return TextureClient::CreateForRawBufferAccess(mAllocator, format,
                                                    mSize, mBackendType,
                                                    mBaseTexFlags);
   }
 
 public:
-  already_AddRefed<BufferTextureClient> CreateB8G8R8AX8() {
+  already_AddRefed<TextureClient> CreateB8G8R8AX8() {
     gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8
                                           : gfx::SurfaceFormat::B8G8R8X8;
     return Create(format);
   }
 
-  already_AddRefed<BufferTextureClient> CreateR8G8B8AX8() {
-    RefPtr<BufferTextureClient> ret;
+  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);
     }
 
@@ -237,17 +238,17 @@ public:
 static already_AddRefed<TextureClient>
 TexClientFromReadback(SharedSurface* src, ISurfaceAllocator* allocator,
                       TextureFlags baseFlags, LayersBackend layersBackend)
 {
   auto backendType = gfx::BackendType::CAIRO;
   TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
                            baseFlags, layersBackend);
 
-  RefPtr<BufferTextureClient> texClient;
+  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;
@@ -283,39 +284,41 @@ TexClientFromReadback(SharedSurface* src
     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");
 
-    uint8_t* lockedBytes = texClient->GetLockedData();
+    MappedTextureData mapped;
+    texClient->BorrowMappedData(mapped);
 
-    // ReadPixels from the current FB into lockedBits.
+    // ReadPixels from the current FB into mapped.data.
     auto width = src->mSize.width;
     auto height = src->mSize.height;
 
     {
       ScopedPackAlignment autoAlign(gl, 4);
 
-      gl->raw_fReadPixels(0, 0, width, height, readFormat, readType, lockedBytes);
+      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 = lockedBytes;
+      uint8_t* itr = mapped.data;
       for (size_t i = 0; i < pixels; i++) {
         SwapRB_R8G8B8A8(itr);
         itr += 4;
       }
 
       texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
     }
   }
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -193,17 +193,17 @@ uint64_t
 CompositableClient::GetAsyncID() const
 {
   if (mCompositableChild) {
     return mCompositableChild->mAsyncID;
   }
   return 0; // zero is always an invalid async ID
 }
 
-already_AddRefed<BufferTextureClient>
+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);
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -18,17 +18,16 @@
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/layers/TextureClientRecycleAllocator.h" // for TextureClientRecycleAllocator
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
-class BufferTextureClient;
 class ImageBridgeChild;
 class ImageContainer;
 class CompositableForwarder;
 class CompositableChild;
 class PCompositableChild;
 
 /**
  * Handle RemoveTextureFromCompositableAsync() transaction.
@@ -131,17 +130,17 @@ public:
                     const char* aPrefix="",
                     bool aDumpHtml=false,
                     TextureDumpMode aCompress=TextureDumpMode::Compress) {};
 
   virtual TextureInfo GetTextureInfo() const = 0;
 
   LayersBackend GetCompositorBackendType() const;
 
-  already_AddRefed<BufferTextureClient>
+  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,
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -187,17 +187,17 @@ ImageClientSingle::UpdateImage(ImageCont
           return false;
         }
 
         TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
         if (!autoLock.Succeeded()) {
           return false;
         }
 
-        bool status = texture->AsTextureClientYCbCr()->UpdateYCbCr(*data);
+        bool status = UpdateYCbCrTextureClient(texture, *data);
         MOZ_ASSERT(status);
         if (!status) {
           return false;
         }
       } else if (image->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
                  image->GetFormat() == ImageFormat::EGLIMAGE) {
         gfx::IntSize size = image->GetSize();
 
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -21,16 +21,17 @@
 #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"
 
 #ifdef XP_WIN
 #include "mozilla/layers/TextureD3D9.h"
 #include "mozilla/layers/TextureD3D11.h"
 #include "mozilla/layers/TextureDIB.h"
 #include "gfxWindowsPlatform.h"
 #include "gfx2DGlue.h"
 #endif
@@ -187,16 +188,26 @@ TextureChild::ActorDestroy(ActorDestroyR
   if (mTextureClient) {
     mTextureClient->mActor = nullptr;
     mTextureClient->mAllocator = nullptr;
   }
   mWaitForRecycle = nullptr;
   mKeep = nullptr;
 }
 
+ClientTexture::ClientTexture(TextureData* aData, TextureFlags aFlags, ISurfaceAllocator* aAllocator)
+: TextureClient(aAllocator, aFlags)
+, mData(aData)
+, mOpenMode(OpenMode::OPEN_NONE)
+#ifdef DEBUG
+, mExpectedDtRefs(0)
+#endif
+, mIsLocked(false)
+{}
+
 bool
 ClientTexture::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(mValid);
   MOZ_ASSERT(!mIsLocked);
   if (mIsLocked) {
     return mOpenMode == aMode;
   }
@@ -212,17 +223,17 @@ ClientTexture::Unlock()
 {
   MOZ_ASSERT(mValid);
   MOZ_ASSERT(mIsLocked);
   if (!mIsLocked) {
     return;
   }
 
   if (mBorrowedDrawTarget) {
-    MOZ_ASSERT(mBorrowedDrawTarget->refCount() == 1);
+    MOZ_ASSERT(mBorrowedDrawTarget->refCount() <= mExpectedDtRefs);
     if (mOpenMode & OpenMode::OPEN_WRITE) {
       mBorrowedDrawTarget->Flush();
       if (mReadbackSink) {
         RefPtr<SourceSurface> snapshot = mBorrowedDrawTarget->Snapshot();
         RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
         mReadbackSink->ProcessReadback(dataSurf);
       }
     }
@@ -265,34 +276,38 @@ ClientTexture::~ClientTexture()
   delete mData;
 }
 
 void
 ClientTexture::UpdateFromSurface(gfx::SourceSurface* aSurface)
 {
   MOZ_ASSERT(mValid);
   MOZ_ASSERT(mIsLocked);
+  MOZ_ASSERT(aSurface);
 
+  // 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) {
+    if (dt) {
+      dt->CopySurface(aSurface,
+                      gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()),
+                      gfx::IntPoint(0, 0));
       return;
     }
-
-    dt->CopySurface(aSurface,
-                    gfx::IntRect(gfx::IntPoint(0, 0), aSurface->GetSize()),
-                    gfx::IntPoint(0, 0));
-    return;
   }
-
-  if (!mData->UpdateFromSurface(aSurface)) {
-    NS_WARNING("ClientTexture::UpdateFromSurface failed");
-  }
+  NS_WARNING("ClientTexture::UpdateFromSurface failed");
 }
 
 
 already_AddRefed<TextureClient>
 ClientTexture::CreateSimilar(TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const
 {
   MOZ_ASSERT(mValid);
   TextureData* data = mData->CreateSimilar(mAllocator, aFlags, aAllocFlags);
@@ -314,22 +329,47 @@ ClientTexture::BorrowDrawTarget()
   //MOZ_ASSERT(mOpenMode & OpenMode::OPEN_WRITE);
 
   if (!mIsLocked) {
     return nullptr;
   }
 
   if (!mBorrowedDrawTarget) {
     mBorrowedDrawTarget = mData->BorrowDrawTarget();
+#ifdef DEBUG
+    mExpectedDtRefs = mBorrowedDrawTarget ? mBorrowedDrawTarget->refCount() : 0;
+#endif
   }
 
   return mBorrowedDrawTarget;
 }
 
 bool
+ClientTexture::BorrowMappedData(MappedTextureData& aMap)
+{
+  MOZ_ASSERT(mValid);
+
+  // TODO - SharedRGBImage just accesses the buffer without properly locking
+  // the texture. It's bad.
+  //MOZ_ASSERT(mIsLocked);
+  //if (!mIsLocked) {
+  //  return nullptr;
+  //}
+
+  return mData->BorrowMappedData(aMap);
+}
+
+bool
+ClientTexture::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
+{
+  MOZ_ASSERT(mValid);
+  return mData->BorrowMappedYCbCrData(aMap);
+}
+
+bool
 ClientTexture::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
 {
   MOZ_ASSERT(mValid);
   return mData->Serialize(aOutDescriptor);
 }
 
 // static
 PTextureChild*
@@ -475,35 +515,16 @@ DisableGralloc(SurfaceFormat aFormat, co
     return true;
   }
 #endif
 
   return false;
 }
 #endif
 
-static
-already_AddRefed<BufferTextureClient>
-CreateBufferTextureClient(ISurfaceAllocator* aAllocator,
-                          SurfaceFormat aFormat,
-                          TextureFlags aTextureFlags,
-                          gfx::BackendType aMoz2DBackend)
-{
-  if (aAllocator->IsSameProcess()) {
-    RefPtr<BufferTextureClient> result = new MemoryTextureClient(aAllocator, aFormat,
-                                                                 aMoz2DBackend,
-                                                                 aTextureFlags);
-    return result.forget();
-  }
-  RefPtr<BufferTextureClient> result = new ShmemTextureClient(aAllocator, aFormat,
-                                                              aMoz2DBackend,
-                                                              aTextureFlags);
-  return result.forget();
-}
-
 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);
@@ -620,117 +641,94 @@ TextureClient::CreateForDrawing(Composit
     return nullptr;
   }
 
   if (texture) {
     NS_WARNING("Failed to allocate a TextureClient, falling back to BufferTextureClient.");
   }
 
   // Can't do any better than a buffer texture client.
-  texture = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, moz2DBackend);
-
-  if (!texture->AllocateForSurface(aSize, aAllocFlags)) {
-    return nullptr;
-  }
-
-  return texture.forget();
+  return TextureClient::CreateForRawBufferAccess(aAllocator, aFormat, aSize,
+                                                 moz2DBackend, aTextureFlags, aAllocFlags);
 }
 
 // static
-already_AddRefed<BufferTextureClient>
+already_AddRefed<TextureClient>
 TextureClient::CreateForRawBufferAccess(ISurfaceAllocator* aAllocator,
                                         gfx::SurfaceFormat aFormat,
                                         gfx::IntSize aSize,
                                         gfx::BackendType aMoz2DBackend,
                                         TextureFlags aTextureFlags,
                                         TextureAllocationFlags aAllocFlags)
 {
   MOZ_ASSERT(aAllocator->IPCOpen());
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aSize)) {
     return nullptr;
   }
 
-  RefPtr<BufferTextureClient> texture =
-    CreateBufferTextureClient(aAllocator, aFormat,
-                              aTextureFlags, aMoz2DBackend);
-  if (texture) {
-    if (!texture->AllocateForSurface(aSize, aAllocFlags)) {
-      return nullptr;
-    }
+  TextureData* texData = BufferTextureData::Create(aSize, aFormat, aMoz2DBackend,
+                                                   aTextureFlags, aAllocFlags,
+                                                   aAllocator);
+  if (!texData) {
+    return nullptr;
   }
-  return texture.forget();
+
+  return MakeAndAddRef<ClientTexture>(texData, aTextureFlags, aAllocator);
 }
 
 // static
-already_AddRefed<BufferTextureClient>
+already_AddRefed<TextureClient>
 TextureClient::CreateForYCbCr(ISurfaceAllocator* aAllocator,
                               gfx::IntSize aYSize,
                               gfx::IntSize aCbCrSize,
                               StereoMode aStereoMode,
                               TextureFlags aTextureFlags)
 {
-  MOZ_ASSERT(aAllocator->IPCOpen());
-  if (!aAllocator || !aAllocator->IPCOpen()) {
+  // The only reason we allow aAllocator to be null is for gtests
+  MOZ_ASSERT(!aAllocator || aAllocator->IPCOpen());
+  if (aAllocator && !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
   if (!gfx::Factory::AllowedSurfaceSize(aYSize)) {
     return nullptr;
   }
 
-  RefPtr<BufferTextureClient> texture;
-  if (aAllocator->IsSameProcess()) {
-    texture = new MemoryTextureClient(aAllocator, gfx::SurfaceFormat::YUV,
-                                      gfx::BackendType::NONE,
-                                      aTextureFlags);
-  } else {
-    texture = new ShmemTextureClient(aAllocator, gfx::SurfaceFormat::YUV,
-                                     gfx::BackendType::NONE,
-                                     aTextureFlags);
-  }
-
-  if (!texture->AllocateForYCbCr(aYSize, aCbCrSize, aStereoMode)) {
+  TextureData* data = BufferTextureData::CreateForYCbCr(aAllocator, aYSize, aCbCrSize,
+                                                        aStereoMode, aTextureFlags);
+  if (!data) {
     return nullptr;
   }
 
-  return texture.forget();
+  return MakeAndAddRef<ClientTexture>(data, aTextureFlags, aAllocator);
 }
 
 // static
-already_AddRefed<BufferTextureClient>
+already_AddRefed<TextureClient>
 TextureClient::CreateWithBufferSize(ISurfaceAllocator* aAllocator,
-                     gfx::SurfaceFormat aFormat,
-                     size_t aSize,
-                     TextureFlags aTextureFlags)
+                                    gfx::SurfaceFormat aFormat,
+                                    size_t aSize,
+                                    TextureFlags aTextureFlags)
 {
   MOZ_ASSERT(aAllocator->IPCOpen());
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
 
-  RefPtr<BufferTextureClient> texture;
-  if (aAllocator->IsSameProcess()) {
-    texture = new MemoryTextureClient(aAllocator, gfx::SurfaceFormat::YUV,
-                                      gfx::BackendType::NONE,
-                                      aTextureFlags);
-  } else {
-    texture = new ShmemTextureClient(aAllocator, gfx::SurfaceFormat::YUV,
-                                     gfx::BackendType::NONE,
-                                     aTextureFlags);
-  }
-
-  if (!texture->Allocate(aSize)) {
+  TextureData* data = BufferTextureData::CreateWithBufferSize(aAllocator, aFormat, aSize,
+                                                              aTextureFlags);
+  if (!data) {
     return nullptr;
   }
 
-  return texture.forget();
+  return MakeAndAddRef<ClientTexture>(data, aTextureFlags, aAllocator);
 }
 
 TextureClient::TextureClient(ISurfaceAllocator* aAllocator, TextureFlags aFlags)
   : mAllocator(aAllocator)
   , mFlags(aFlags)
   , mShared(false)
   , mValid(true)
   , mAddedToCompositableClient(false)
@@ -862,363 +860,105 @@ TextureClient::PrintInfo(std::stringstre
     aStream << "\n" << pfx.get() << "Surface: ";
     RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
     if (dSurf) {
       aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
     }
   }
 #endif
 }
-bool
-ShmemTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor)
-{
-  MOZ_ASSERT(IsValid());
-  if (!IsAllocated() || GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
-    return false;
-  }
-
-  aDescriptor = SurfaceDescriptorShmem(mShmem, GetFormat());
-
-  return true;
-}
-
-bool
-ShmemTextureClient::Allocate(uint32_t aSize)
-{
-  MOZ_ASSERT(mValid);
-  if (aSize > 0) {
-    SharedMemory::SharedMemoryType memType = OptimalShmemType();
-    mAllocated = GetAllocator()->AllocUnsafeShmem(aSize, memType, &mShmem);
-  }
-  return mAllocated;
-}
-
-uint8_t*
-ShmemTextureClient::GetBuffer() const
-{
-  MOZ_ASSERT(IsValid());
-  if (mAllocated) {
-    return mShmem.get<uint8_t>();
-  }
-  return nullptr;
-}
-
-size_t
-ShmemTextureClient::GetBufferSize() const
-{
-  MOZ_ASSERT(IsValid());
-  return mShmem.Size<uint8_t>();
-}
-
-ShmemTextureClient::ShmemTextureClient(ISurfaceAllocator* aAllocator,
-                                       gfx::SurfaceFormat aFormat,
-                                       gfx::BackendType aMoz2DBackend,
-                                       TextureFlags aFlags)
-  : BufferTextureClient(aAllocator, aFormat, aMoz2DBackend, aFlags)
-  , mAllocated(false)
-{
-  MOZ_COUNT_CTOR(ShmemTextureClient);
-}
-
-ShmemTextureClient::~ShmemTextureClient()
-{
-  MOZ_COUNT_DTOR(ShmemTextureClient);
-  if (ShouldDeallocateInDestructor()) {
-    // if the buffer has never been shared we must deallocate it or ir would
-    // leak.
-    GetAllocator()->DeallocShmem(mShmem);
-  }
-}
 
 bool
-MemoryTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor)
-{
-  MOZ_ASSERT(IsValid());
-  if (!IsAllocated() || GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
-    return false;
-  }
-  aDescriptor = SurfaceDescriptorMemory(reinterpret_cast<uintptr_t>(mBuffer),
-                                        GetFormat());
-  return true;
-}
-
-bool
-MemoryTextureClient::Allocate(uint32_t aSize)
-{
-  MOZ_ASSERT(!mBuffer);
-  mBuffer = new (fallible) uint8_t[aSize];
-  if (!mBuffer) {
-    NS_WARNING("Failed to allocate buffer");
-    return false;
-  }
-  GfxMemoryImageReporter::DidAlloc(mBuffer);
-  mBufSize = aSize;
-  return true;
-}
-
-MemoryTextureClient::MemoryTextureClient(ISurfaceAllocator* aAllocator,
-                                         gfx::SurfaceFormat aFormat,
-                                         gfx::BackendType aMoz2DBackend,
-                                         TextureFlags aFlags)
-  : BufferTextureClient(aAllocator, aFormat, aMoz2DBackend, aFlags)
-  , mBuffer(nullptr)
-  , mBufSize(0)
-{
-  MOZ_COUNT_CTOR(MemoryTextureClient);
-}
-
-MemoryTextureClient::~MemoryTextureClient()
+UpdateYCbCrTextureClient(TextureClient* aTexture, const PlanarYCbCrData& aData)
 {
-  MOZ_COUNT_DTOR(MemoryTextureClient);
-  if (mBuffer && ShouldDeallocateInDestructor()) {
-    // if the buffer has never been shared we must deallocate it or it would
-    // leak.
-    GfxMemoryImageReporter::WillFree(mBuffer);
-    delete [] mBuffer;
-  }
-}
-
-BufferTextureClient::BufferTextureClient(ISurfaceAllocator* aAllocator,
-                                         gfx::SurfaceFormat aFormat,
-                                         gfx::BackendType aMoz2DBackend,
-                                         TextureFlags aFlags)
-  : TextureClient(aAllocator, aFlags)
-  , mFormat(aFormat)
-  , mBackend(aMoz2DBackend)
-  , mOpenMode(OpenMode::OPEN_NONE)
-  , mLocked(false)
-{}
+  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);
 
-BufferTextureClient::~BufferTextureClient()
-{}
-
-already_AddRefed<TextureClient>
-BufferTextureClient::CreateSimilar(TextureFlags aFlags,
-                                   TextureAllocationFlags aAllocFlags) const
-{
-  // This may return null
-  RefPtr<BufferTextureClient> newBufferTex = TextureClient::CreateForRawBufferAccess(
-    mAllocator, mFormat, mSize, mBackend, mFlags | aFlags, aAllocFlags
-  );
-
-  return newBufferTex.forget();
-}
-
-bool
-BufferTextureClient::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
-{
-  MOZ_ASSERT(IsValid());
-  MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV, "This textureClient cannot use YCbCr data");
-  MOZ_ASSERT(aSize.width > 0 && aSize.height > 0);
-
-  if (aSize.width <= 0 || aSize.height <= 0) {
-    gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
+  MappedYCbCrTextureData mapped;
+  if (!aTexture->BorrowMappedYCbCrData(mapped)) {
+    NS_WARNING("Failed to extract YCbCr info!");
     return false;
   }
 
-  uint32_t bufSize = ImageDataSerializer::ComputeMinBufferSize(aSize, mFormat);
-  if (!bufSize || !Allocate(bufSize)) {
-    return false;
-  }
-
-  if (aFlags & ALLOC_CLEAR_BUFFER) {
-    memset(GetBuffer(), 0, bufSize);
-  }
-  if (aFlags & ALLOC_CLEAR_BUFFER_WHITE) {
-    memset(GetBuffer(), 0xFF, bufSize);
-  }
-
-  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
-  serializer.InitializeBufferInfo(aSize, mFormat);
-  mSize = aSize;
-  return true;
-}
-
-gfx::DrawTarget*
-BufferTextureClient::BorrowDrawTarget()
-{
-  MOZ_ASSERT(IsValid());
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mLocked, "BorrowDrawTarget should be called on locked textures only");
-  if (!mLocked) {
-    return nullptr;
-  }
-
-  if (mDrawTarget) {
-    mDrawTarget->SetTransform(Matrix());
-    return mDrawTarget;
-  }
-
-  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
-  if (!serializer.IsValid()) {
-    gfxCriticalNote << "Invalid serializer " << IsValid() << ", " << IsLocked() << ", " << GetBufferSize();
-    return nullptr;
-  }
-
-  mDrawTarget = serializer.GetAsDrawTarget(mBackend);
-  if (mDrawTarget) {
-    return mDrawTarget;
-  }
-
-  mDrawTarget = serializer.GetAsDrawTarget(BackendType::CAIRO);
-  if (!mDrawTarget) {
-    gfxCriticalNote << "BorrowDrawTarget failure, original backend " << (int)mBackend;
-  }
-
-  return mDrawTarget;
-}
-
-void
-BufferTextureClient::UpdateFromSurface(gfx::SourceSurface* aSurface)
-{
-  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
-
-  RefPtr<DataSourceSurface> surface = serializer.GetAsSurface();
-
-  if (!surface) {
-    gfxCriticalError() << "Failed to get serializer as surface!";
-    return;
-  }
-
-  RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
-
-  if (!srcSurf) {
-    gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface.";
-    return;
-  }
+  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 (surface->GetSize() != srcSurf->GetSize() || surface->GetFormat() != srcSurf->GetFormat()) {
-    gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
-    return;
-  }
-
-  DataSourceSurface::MappedSurface sourceMap;
-  DataSourceSurface::MappedSurface destMap;
-  if (!srcSurf->Map(DataSourceSurface::READ, &sourceMap)) {
-    gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
-    return;
-  }
-
-  if (!surface->Map(DataSourceSurface::WRITE, &destMap)) {
-    srcSurf->Unmap();
-    gfxCriticalError() << "Failed to map destination surface for UpdateFromSurface.";
-    return;
-  }
-
-
-  for (int y = 0; y < srcSurf->GetSize().height; y++) {
-    memcpy(destMap.mData + destMap.mStride * y,
-           sourceMap.mData + sourceMap.mStride * y,
-           srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
-  }
-
-  srcSurf->Unmap();
-  surface->Unmap();
-}
-
-bool
-BufferTextureClient::Lock(OpenMode aMode)
-{
-  MOZ_ASSERT(!mLocked, "The TextureClient is already Locked!");
-  mOpenMode = aMode;
-  mLocked = IsValid() && IsAllocated();;
-  return mLocked;
-}
-
-void
-BufferTextureClient::Unlock()
-{
-  MOZ_ASSERT(mLocked, "The TextureClient is already Unlocked!");
-  mLocked = false;
-  if (!mDrawTarget) {
-    return;
-  }
-
-  // see the comment on TextureClient::BorrowDrawTarget.
-  // This DrawTarget is internal to the TextureClient and is only exposed to the
-  // outside world between Lock() and Unlock(). This assertion checks that no outside
-  // reference remains by the time Unlock() is called.
-  MOZ_ASSERT(mDrawTarget->refCount() == 1);
-
-  if (mReadbackSink) {
-    RefPtr<SourceSurface> snapshot = mDrawTarget->Snapshot();
-    RefPtr<DataSourceSurface> dataSurf = snapshot->GetDataSurface();
-    mReadbackSink->ProcessReadback(dataSurf);
-  }
-
-  mDrawTarget->Flush();
-}
-
-bool
-BufferTextureClient::UpdateYCbCr(const PlanarYCbCrData& aData)
-{
-  MOZ_ASSERT(mLocked);
-  MOZ_ASSERT(mFormat == gfx::SurfaceFormat::YUV, "This textureClient can only use YCbCr data");
-  MOZ_ASSERT(!IsImmutable());
-  MOZ_ASSERT(IsValid());
-  MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip);
-
-  YCbCrImageDataSerializer serializer(GetBuffer(), GetBufferSize());
-  MOZ_ASSERT(serializer.IsValid());
-  if (!serializer.CopyData(aData.mYChannel, aData.mCbChannel, aData.mCrChannel,
-                           aData.mYSize, aData.mYStride,
-                           aData.mCbCrSize, aData.mCbCrStride,
-                           aData.mYSkip, aData.mCbSkip)) {
+  if (!srcData.CopyInto(mapped)) {
     NS_WARNING("Failed to copy image data!");
     return false;
   }
 
-  if (TextureRequiresLocking(mFlags)) {
+  if (TextureRequiresLocking(aTexture->GetFlags())) {
     // We don't have support for proper locking yet, so we'll
     // have to be immutable instead.
-    MarkImmutable();
+    aTexture->MarkImmutable();
   }
   return true;
 }
 
-bool
-BufferTextureClient::AllocateForYCbCr(gfx::IntSize aYSize,
-                                      gfx::IntSize aCbCrSize,
-                                      StereoMode aStereoMode)
-{
-  MOZ_ASSERT(IsValid());
-
-  size_t bufSize = YCbCrImageDataSerializer::ComputeMinBufferSize(aYSize,
-                                                                  aCbCrSize);
-  if (!bufSize || !Allocate(bufSize)) {
-    return false;
-  }
-  YCbCrImageDataSerializer serializer(GetBuffer(), GetBufferSize());
-  serializer.InitializeBufferInfo(aYSize,
-                                  aCbCrSize,
-                                  aStereoMode);
-  mSize = aYSize;
-  return true;
-}
-
-uint8_t*
-BufferTextureClient::GetLockedData() const
-{
-  MOZ_ASSERT(IsLocked());
-
-  ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
-  MOZ_ASSERT(serializer.IsValid());
-
-  return serializer.GetData();
-}
-
 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
 }
 
+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
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -44,17 +44,18 @@ namespace layers {
 class AsyncTransactionWaiter;
 class CompositableForwarder;
 class ISurfaceAllocator;
 class CompositableClient;
 struct PlanarYCbCrData;
 class Image;
 class PTextureChild;
 class TextureChild;
-class BufferTextureClient;
+struct RawTextureBuffer;
+class RawYCbCrTextureBuffer;
 class TextureClient;
 class TextureClientRecycleAllocator;
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
 class TextureClientPool;
 #endif
 class KeepAlive;
 class GrallocTextureClientOGL;
 
@@ -91,41 +92,16 @@ public:
   virtual SyncType GetSyncType() = 0;
   virtual void FinalizeFrame() = 0;
 
 protected:
   SyncObject() { }
 };
 
 /**
- * Interface for TextureClients that can be updated using YCbCr data.
- */
-class TextureClientYCbCr
-{
-public:
-  /**
-   * Copy aData into this texture client.
-   *
-   * This must never be called on a TextureClient that is not sucessfully locked.
-   */
-  virtual bool UpdateYCbCr(const PlanarYCbCrData& aData) = 0;
-
-  /**
-   * Allocates for a given surface size, taking into account the pixel format
-   * which is part of the state of the TextureClient.
-   *
-   * Does not clear the surface, since we consider that the surface
-   * be painted entirely with opaque content.
-   */
-  virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
-                                gfx::IntSize aCbCrSize,
-                                StereoMode aStereoMode) = 0;
-};
-
-/**
  * 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:
@@ -142,16 +118,53 @@ protected:
 };
 
 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);
+  }
+};
+
 /**
  * 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:
@@ -182,38 +195,38 @@ public:
   static already_AddRefed<TextureClient>
   CreateForDrawing(CompositableForwarder* aAllocator,
                    gfx::SurfaceFormat aFormat,
                    gfx::IntSize aSize,
                    BackendSelector aSelector,
                    TextureFlags aTextureFlags,
                    TextureAllocationFlags flags = ALLOC_DEFAULT);
 
-  // Creates and allocates a BufferTextureClient supporting the YCbCr format.
-  static already_AddRefed<BufferTextureClient>
+  // Creates and allocates a TextureClient supporting the YCbCr format.
+  static already_AddRefed<TextureClient>
   CreateForYCbCr(ISurfaceAllocator* aAllocator,
                  gfx::IntSize aYSize,
                  gfx::IntSize aCbCrSize,
                  StereoMode aStereoMode,
                  TextureFlags aTextureFlags);
 
-  // Creates and allocates a BufferTextureClient (can beaccessed through raw
+  // Creates and allocates a TextureClient (can be accessed through raw
   // pointers).
-  static already_AddRefed<BufferTextureClient>
+  static already_AddRefed<TextureClient>
   CreateForRawBufferAccess(ISurfaceAllocator* aAllocator,
                            gfx::SurfaceFormat aFormat,
                            gfx::IntSize aSize,
                            gfx::BackendType aMoz2dBackend,
                            TextureFlags aTextureFlags,
                            TextureAllocationFlags flags = ALLOC_DEFAULT);
 
-  // Creates and allocates a BufferTextureClient (can beaccessed through raw
+  // 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<BufferTextureClient>
+  static already_AddRefed<TextureClient>
   CreateWithBufferSize(ISurfaceAllocator* aAllocator,
                        gfx::SurfaceFormat aFormat,
                        size_t aSize,
                        TextureFlags aTextureFlags);
 
   // Creates and allocates a TextureClient of the same type.
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
@@ -229,33 +242,34 @@ public:
    * TextureClients that can expose a DrawTarget should override this method.
    */
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags flags = ALLOC_DEFAULT)
   {
     return false;
   }
 
-  virtual TextureClientYCbCr* AsTextureClientYCbCr() { return nullptr; }
   virtual GrallocTextureClientOGL* AsGrallocTextureClientOGL() { return nullptr; }
 
   /**
    * 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.
    */
   virtual bool Lock(OpenMode aMode) { return IsValid(); }
 
   virtual void Unlock() {}
 
   virtual bool IsLocked() const = 0;
 
   virtual bool CanExposeDrawTarget() const { return false; }
 
+  virtual bool CanExposeMappedData() const { return false; }
+
   /**
    * 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
@@ -275,16 +289,23 @@ public:
    *   // use the draw target ...
    * }
    * texture->Unlock();
    *
    */
   virtual gfx::DrawTarget* BorrowDrawTarget() { return nullptr; }
 
   /**
+   * Similar to BorrowDrawTarget but provides direct access to the texture's bits
+   * instead of a DrawTarget.
+   */
+  virtual bool BorrowMappedData(MappedTextureData&) { return false; }
+  virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
+
+  /**
    * This function can be used to update the contents of the TextureClient
    * off the main thread.
    */
   virtual void UpdateFromSurface(gfx::SourceSurface* aSurface) { MOZ_CRASH(); }
 
   // TextureClients that can expose a DrawTarget should override this method.
   virtual gfx::SurfaceFormat GetFormat() const
   {
@@ -580,65 +601,72 @@ public:
   virtual gfx::SurfaceFormat GetFormat() const = 0;
 
   virtual bool Lock(OpenMode aMode) = 0;
 
   virtual void Unlock() = 0;
 
   virtual bool SupportsMoz2D() const { return false; }
 
+  virtual bool CanExposeMappedData() const { return false; }
+
   virtual bool HasInternalBuffer() const = 0;
 
   virtual bool HasSynchronization() const { return false; }
 
   virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() { return nullptr; }
 
+  virtual bool BorrowMappedData(MappedTextureData&) { return false; }
+
+  virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) { return false; }
+
   virtual void Deallocate(ISurfaceAllocator* aAllocator) = 0;
 
   virtual bool Serialize(SurfaceDescriptor& aDescriptor) = 0;
 
   virtual TextureData*
   CreateSimilar(ISurfaceAllocator* aAllocator,
                 TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const { return nullptr; }
 
   virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) { return false; };
 };
 
 /// temporary class that will be merged back into TextureClient when all texture implementations
 /// are based on TextureData.
 class ClientTexture : public TextureClient {
 public:
-  ClientTexture(TextureData* aData, TextureFlags aFlags, ISurfaceAllocator* aAllocator)
-  : TextureClient(aAllocator, aFlags)
-  , mData(aData)
-  , mOpenMode(OpenMode::OPEN_NONE)
-  , mIsLocked(false)
-  {}
+  ClientTexture(TextureData* aData, TextureFlags aFlags, ISurfaceAllocator* aAllocator);
 
   ~ClientTexture();
 
   virtual bool CanExposeDrawTarget() const override { return mData->SupportsMoz2D(); }
 
+  virtual bool CanExposeMappedData() const override { return mData->CanExposeMappedData(); }
+
   virtual bool HasInternalBuffer() const override;
 
   virtual bool HasSynchronization() const { return mData->HasSynchronization(); }
 
   virtual gfx::IntSize GetSize() const override;
 
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual bool Lock(OpenMode aMode) override;
 
   virtual void Unlock() override;
 
   virtual bool IsLocked() const override { return mIsLocked; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
+  virtual bool BorrowMappedData(MappedTextureData&) override;
+
+  virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData&) override;
+
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) override;
 
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
   virtual void UpdateFromSurface(gfx::SourceSurface* aSurface) override;
 
@@ -646,16 +674,17 @@ public:
   virtual bool IsAllocated() const override { return true; }
 
 protected:
   TextureData* mData;
   RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
   RefPtr<TextureReadbackSink> mReadbackSink;
 
   OpenMode mOpenMode;
+  DebugOnly<uint32_t> mExpectedDtRefs;
   bool mIsLocked;
 };
 
 /**
  * Task that releases TextureClient pointer on a specified thread.
  */
 class TextureClientReleaseTask : public Task
 {
@@ -668,153 +697,16 @@ public:
     {
         mTextureClient = nullptr;
     }
 
 private:
     RefPtr<TextureClient> mTextureClient;
 };
 
-/**
- * TextureClient that wraps a random access buffer such as a Shmem or raw memory.
- * This class must be inherited to implement the memory allocation and access bits.
- * (see ShmemTextureClient and MemoryTextureClient)
- */
-class BufferTextureClient : public TextureClient
-                          , public TextureClientYCbCr
-{
-public:
-  BufferTextureClient(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat,
-                      gfx::BackendType aBackend, TextureFlags aFlags);
-
-  virtual ~BufferTextureClient();
-
-  virtual bool IsAllocated() const override = 0;
-
-  virtual uint8_t* GetBuffer() const = 0;
-
-  virtual gfx::IntSize GetSize() const override { return mSize; }
-
-  virtual bool Lock(OpenMode aMode) override;
-
-  virtual void Unlock() override;
-
-  virtual bool IsLocked() const override { return mLocked; }
-
-  uint8_t* GetLockedData() const;
-
-  virtual bool CanExposeDrawTarget() const override { return true; }
-
-  virtual gfx::DrawTarget* BorrowDrawTarget() override;
-
-  virtual void UpdateFromSurface(gfx::SourceSurface* aSurface) override;
-
-  virtual bool AllocateForSurface(gfx::IntSize aSize,
-                                  TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
-
-  // TextureClientYCbCr
-
-  virtual TextureClientYCbCr* AsTextureClientYCbCr() override { return this; }
-
-  virtual bool UpdateYCbCr(const PlanarYCbCrData& aData) override;
-
-  virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
-                                gfx::IntSize aCbCrSize,
-                                StereoMode aStereoMode) override;
-
-  virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
-
-  // XXX - Bug 908196 - Make Allocate(uint32_t) and GetBufferSize() protected.
-  // these two methods should only be called by methods of BufferTextureClient
-  // that are overridden in GrallocTextureClient (which does not implement the
-  // two methods below)
-  virtual bool Allocate(uint32_t aSize) = 0;
-
-  virtual size_t GetBufferSize() const = 0;
-
-  virtual bool HasInternalBuffer() const override { return true; }
-
-  virtual already_AddRefed<TextureClient>
-  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
-                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
-
-protected:
-  RefPtr<gfx::DrawTarget> mDrawTarget;
-  gfx::SurfaceFormat mFormat;
-  gfx::IntSize mSize;
-  gfx::BackendType mBackend;
-  OpenMode mOpenMode;
-  bool mLocked;
-};
-
-/**
- * TextureClient that wraps shared memory.
- * the corresponding texture on the host side is ShmemTextureHost.
- */
-class ShmemTextureClient : public BufferTextureClient
-{
-public:
-  ShmemTextureClient(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat,
-                     gfx::BackendType aBackend, TextureFlags aFlags);
-
-protected:
-  ~ShmemTextureClient();
-
-public:
-  virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) override;
-
-  virtual bool Allocate(uint32_t aSize) override;
-
-  virtual uint8_t* GetBuffer() const override;
-
-  virtual size_t GetBufferSize() const override;
-
-  virtual bool IsAllocated() const override { return mAllocated; }
-
-  virtual bool HasInternalBuffer() const override { return true; }
-
-  mozilla::ipc::Shmem& GetShmem() { return mShmem; }
-
-protected:
-  mozilla::ipc::Shmem mShmem;
-  bool mAllocated;
-};
-
-/**
- * TextureClient that wraps raw memory.
- * The corresponding texture on the host side is MemoryTextureHost.
- * Can obviously not be used in a cross process setup.
- */
-class MemoryTextureClient : public BufferTextureClient
-{
-public:
-  MemoryTextureClient(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat aFormat,
-                      gfx::BackendType aBackend, TextureFlags aFlags);
-
-protected:
-  ~MemoryTextureClient();
-
-public:
-  virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) override;
-
-  virtual bool Allocate(uint32_t aSize) override;
-
-  virtual uint8_t* GetBuffer() const override { return mBuffer; }
-
-  virtual size_t GetBufferSize() const override { return mBufSize; }
-
-  virtual bool IsAllocated() const override { return mBuffer != nullptr; }
-
-  virtual bool HasInternalBuffer() const override { return true; }
-
-protected:
-  uint8_t* mBuffer;
-  size_t mBufSize;
-};
-
 // 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
@@ -855,12 +747,15 @@ 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
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -8,17 +8,18 @@
 #include <stdio.h>                      // for printf
 #include "gfx2DGlue.h"                  // for Moz2D transition helpers
 #include "ISurfaceAllocator.h"          // for ISurfaceAllocator, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat::SurfaceFormat::YUV
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
-#include "mozilla/layers/TextureClient.h"  // for BufferTextureClient, etc
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/BufferTexture.h"
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 #include "mozilla/layers/ImageBridgeChild.h"  // for ImageBridgeChild
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsISupportsImpl.h"            // for Image::AddRef
 #include "mozilla/ipc/Shmem.h"
 
 namespace mozilla {
 namespace layers {
@@ -60,17 +61,19 @@ TextureClient*
 SharedPlanarYCbCrImage::GetTextureClient(CompositableClient* aClient)
 {
   return mTextureClient.get();
 }
 
 uint8_t*
 SharedPlanarYCbCrImage::GetBuffer()
 {
-  return mTextureClient ? mTextureClient->GetBuffer() : nullptr;
+  // This should never be used
+  MOZ_ASSERT(false);
+  return nullptr;
 }
 
 already_AddRefed<gfx::SourceSurface>
 SharedPlanarYCbCrImage::GetAsSourceSurface()
 {
   if (!mTextureClient) {
     NS_WARNING("Can't get as surface");
     return nullptr;
@@ -84,25 +87,23 @@ SharedPlanarYCbCrImage::SetData(const Pl
   // If mTextureClient has not already been allocated (through Allocate(aData))
   // allocate it. This code path is slower than the one used when Allocate has
   // been called since it will trigger a full copy.
   PlanarYCbCrData data = aData;
   if (!mTextureClient && !Allocate(data)) {
     return false;
   }
 
-  MOZ_ASSERT(mTextureClient->AsTextureClientYCbCr());
-
   TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY);
   if (!autoLock.Succeeded()) {
     MOZ_ASSERT(false, "Failed to lock the texture.");
     return false;
   }
 
-  if (!mTextureClient->AsTextureClientYCbCr()->UpdateYCbCr(aData)) {
+  if (!UpdateYCbCrTextureClient(mTextureClient, aData)) {
     MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
     return false;
   }
   mTextureClient->MarkImmutable();
   return true;
 }
 
 // needs to be overriden because the parent class sets mBuffer which we
@@ -123,18 +124,28 @@ SharedPlanarYCbCrImage::AllocateAndGetNe
   // get new buffer _without_ setting mBuffer.
   if (!mTextureClient) {
     return nullptr;
   }
 
   // update buffer size
   mBufferSize = size;
 
-  YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer(), mTextureClient->GetBufferSize());
-  return serializer.GetData();
+  MappedYCbCrTextureData mapped;
+  if (mTextureClient->BorrowMappedYCbCrData(mapped)) {
+    // The caller expects a pointer to the beginning of the writable part of the
+    // buffer (after the metadata) which is where the y channel starts by default.
+    // The caller might choose to write the y channel at a different offset and
+    // if it does so, it will also update the metadata.
+    // Anyway, we return the y channel here but the intent is to obtain the start of
+    // the writable part of the buffer.
+    return mapped.y.data;
+  } else {
+    MOZ_CRASH();
+  }
 }
 
 bool
 SharedPlanarYCbCrImage::SetDataNoCopy(const Data &aData)
 {
   MOZ_ASSERT(mTextureClient, "This Image should have already allocated data");
   if (!mTextureClient) {
     return false;
@@ -142,17 +153,21 @@ SharedPlanarYCbCrImage::SetDataNoCopy(co
   mData = aData;
   mSize = aData.mPicSize;
   /* SetDataNoCopy is used to update YUV plane offsets without (re)allocating
    * memory previously allocated with AllocateAndGetNewBuffer().
    * serializer.GetData() returns the address of the memory previously allocated
    * with AllocateAndGetNewBuffer(), that we subtract from the Y, Cb, Cr
    * channels to compute 0-based offsets to pass to InitializeBufferInfo.
    */
-  YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer(), mTextureClient->GetBufferSize());
+  MappedYCbCrTextureData mapped;
+  if(!mTextureClient->BorrowMappedYCbCrData(mapped)) {
+    MOZ_CRASH();
+  }
+  YCbCrImageDataSerializer serializer(mapped.metadata, mBufferSize);
   uint8_t *base = serializer.GetData();
   uint32_t yOffset = aData.mYChannel - base;
   uint32_t cbOffset = aData.mCbChannel - base;
   uint32_t crOffset = aData.mCrChannel - base;
   serializer.InitializeBufferInfo(yOffset,
                                   cbOffset,
                                   crOffset,
                                   aData.mYStride,
@@ -178,25 +193,28 @@ SharedPlanarYCbCrImage::Allocate(PlanarY
                                                  aData.mYSize, aData.mCbCrSize,
                                                  aData.mStereoMode,
                                                  mCompositable->GetTextureFlags());
   if (!mTextureClient) {
     NS_WARNING("SharedPlanarYCbCrImage::Allocate failed.");
     return false;
   }
 
-  YCbCrImageDataSerializer serializer(mTextureClient->GetBuffer(), mTextureClient->GetBufferSize());
-  serializer.InitializeBufferInfo(aData.mYSize,
-                                  aData.mCbCrSize,
-                                  aData.mStereoMode);
-  MOZ_ASSERT(serializer.IsValid());
+  MappedYCbCrTextureData mapped;
+  // The locking here is sort of a lie. The SharedPlanarYCbCrImage just pulls
+  // pointers out of the TextureClient and keeps them around, which works only
+  // because the underlyin BufferTextureData is always mapped in memory even outside
+  // of the lock/unlock interval. That's sad and new code should follow this example.
+  if (!mTextureClient->Lock(OpenMode::OPEN_READ) || !mTextureClient->BorrowMappedYCbCrData(mapped)) {
+    MOZ_CRASH();
+  }
 
-  aData.mYChannel = serializer.GetYData();
-  aData.mCbChannel = serializer.GetCbData();
-  aData.mCrChannel = serializer.GetCrData();
+  aData.mYChannel = mapped.y.data;
+  aData.mCbChannel = mapped.cb.data;
+  aData.mCrChannel = mapped.cr.data;
 
   // copy some of aData's values in mData (most of them)
   mData.mYChannel = aData.mYChannel;
   mData.mCbChannel = aData.mCbChannel;
   mData.mCrChannel = aData.mCrChannel;
   mData.mYSize = aData.mYSize;
   mData.mCbCrSize = aData.mCbCrSize;
   mData.mPicX = aData.mPicX;
@@ -213,13 +231,15 @@ SharedPlanarYCbCrImage::Allocate(PlanarY
 
   // do not set mBuffer like in PlanarYCbCrImage because the later
   // will try to manage this memory without knowing it belongs to a
   // shmem.
   mBufferSize = YCbCrImageDataSerializer::ComputeMinBufferSize(mData.mYSize,
                                                                mData.mCbCrSize);
   mSize = mData.mPicSize;
 
+  mTextureClient->Unlock();
+
   return mBufferSize > 0;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.h
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.h
@@ -13,17 +13,16 @@
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR
 
 #ifndef MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
 #define MOZILLA_LAYERS_SHAREDPLANARYCBCRIMAGE_H
 
 namespace mozilla {
 namespace layers {
 
-class BufferTextureClient;
 class ImageClient;
 class TextureClient;
 
 class SharedPlanarYCbCrImage : public PlanarYCbCrImage
 {
 public:
   explicit SharedPlanarYCbCrImage(ImageClient* aCompositable);
 
@@ -46,16 +45,16 @@ public:
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
 
 private:
-  RefPtr<BufferTextureClient> mTextureClient;
+  RefPtr<TextureClient> mTextureClient;
   RefPtr<ImageClient> mCompositable;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/SharedRGBImage.cpp
+++ b/gfx/layers/ipc/SharedRGBImage.cpp
@@ -3,19 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SharedRGBImage.h"
 #include "ImageTypes.h"                 // for ImageFormat::SHARED_RGB, etc
 #include "Shmem.h"                      // for Shmem
 #include "gfx2DGlue.h"                  // for ImageFormatToSurfaceFormat, etc
 #include "gfxPlatform.h"                // for gfxPlatform, gfxImageFormat
 #include "mozilla/gfx/Point.h"          // for IntSIze
+#include "mozilla/layers/BufferTexture.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator, etc
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
-#include "mozilla/layers/ImageDataSerializer.h"  // for ImageDataSerializer
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/TextureClient.h"  // for BufferTextureClient, etc
 #include "mozilla/layers/ImageBridgeChild.h"  // for ImageBridgeChild
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Image::AddRef, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
@@ -82,37 +82,29 @@ SharedRGBImage::Allocate(gfx::IntSize aS
                                                             gfx::BackendType::NONE,
                                                             TextureFlags::DEFAULT);
   return !!mTextureClient;
 }
 
 uint8_t*
 SharedRGBImage::GetBuffer()
 {
-  if (!mTextureClient) {
-    return nullptr;
+  MappedTextureData mapped;
+  if (mTextureClient && mTextureClient->BorrowMappedData(mapped)) {
+    return mapped.data;
   }
-
-  ImageDataSerializer serializer(mTextureClient->GetBuffer(), mTextureClient->GetBufferSize());
-  return serializer.GetData();
+  return 0;
 }
 
 gfx::IntSize
 SharedRGBImage::GetSize()
 {
   return mSize;
 }
 
-size_t
-SharedRGBImage::GetBufferSize()
-{
-  return mTextureClient ? mTextureClient->GetBufferSize()
-                        : 0;
-}
-
 TextureClient*
 SharedRGBImage::GetTextureClient(CompositableClient* aClient)
 {
   return mTextureClient.get();
 }
 
 already_AddRefed<gfx::SourceSurface>
 SharedRGBImage::GetAsSourceSurface()
--- a/gfx/layers/ipc/SharedRGBImage.h
+++ b/gfx/layers/ipc/SharedRGBImage.h
@@ -13,17 +13,16 @@
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "nsCOMPtr.h"                   // for already_AddRefed
 
 namespace mozilla {
 namespace layers {
 
-class BufferTextureClient;
 class ImageClient;
 class TextureClient;
 
 already_AddRefed<Image> CreateSharedRGBImage(ImageContainer* aImageContainer,
                                              gfx::IntSize aSize,
                                              gfxImageFormat aImageFormat);
 
 /**
@@ -40,23 +39,21 @@ protected:
 
 public:
   virtual TextureClient* GetTextureClient(CompositableClient* aClient) override;
 
   virtual uint8_t* GetBuffer() override;
 
   gfx::IntSize GetSize() override;
 
-  size_t GetBufferSize();
-
   already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   bool Allocate(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
 private:
   gfx::IntSize mSize;
   RefPtr<ImageClient> mCompositable;
-  RefPtr<BufferTextureClient> mTextureClient;
+  RefPtr<TextureClient> mTextureClient;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -111,16 +111,17 @@ EXPORTS.mozilla.layers += [
     'apz/util/InputAPZContext.h',
     'AsyncCanvasRenderer.h',
     'AtomicRefCountedWithFinalize.h',
     'AxisPhysicsModel.h',
     'AxisPhysicsMSDModel.h',
     'basic/BasicCompositor.h',
     'basic/MacIOSurfaceTextureHostBasic.h',
     'basic/TextureHostBasic.h',
+    'BufferTexture.h',
     'client/CanvasClient.h',
     'client/CompositableClient.h',
     'client/ContentClient.h',
     'client/ImageClient.h',
     'client/SingleTiledContentClient.h',
     'client/TextureClient.h',
     'client/TextureClientPool.h',
     'client/TextureClientRecycleAllocator.h',
@@ -265,16 +266,17 @@ UNIFIED_SOURCES += [
     'basic/BasicColorLayer.cpp',
     'basic/BasicCompositor.cpp',
     'basic/BasicContainerLayer.cpp',
     'basic/BasicImages.cpp',
     'basic/BasicLayerManager.cpp',
     'basic/BasicLayersImpl.cpp',
     'basic/BasicPaintedLayer.cpp',
     'basic/TextureHostBasic.cpp',
+    'BufferTexture.cpp',
     'BufferUnrotate.cpp',
     'client/CanvasClient.cpp',
     'client/ClientCanvasLayer.cpp',
     'client/ClientColorLayer.cpp',
     'client/ClientContainerLayer.cpp',
     'client/ClientImageLayer.cpp',
     'client/ClientLayerManager.cpp',
     'client/ClientPaintedLayer.cpp',
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -107,16 +107,17 @@ GrallocTextureClientOGL::WaitForBufferOw
      mReleaseFenceHandle = FenceHandle();
    }
 #endif
 }
 
 bool
 GrallocTextureClientOGL::Lock(OpenMode aMode)
 {
+  MOZ_ASSERT(!mIsLocked);
   MOZ_ASSERT(IsValid());
   if (!IsValid() || !IsAllocated()) {
     return false;
   }
 
   if (mMappedBuffer) {
     return true;
   }
@@ -143,38 +144,32 @@ GrallocTextureClientOGL::Lock(OpenMode a
   int32_t rv = mGraphicBuffer->lock(usage,
                                     reinterpret_cast<void**>(&mMappedBuffer));
 #endif
   if (rv) {
     mMappedBuffer = nullptr;
     NS_WARNING("Couldn't lock graphic buffer");
     return false;
   }
-  return BufferTextureClient::Lock(aMode);
+  mIsLocked = true;
+  return true;
 }
 
 void
 GrallocTextureClientOGL::Unlock()
 {
-  BufferTextureClient::Unlock();
+  MOZ_ASSERT(mIsLocked);
+  mIsLocked = false;
   mDrawTarget = nullptr;
   if (mMappedBuffer) {
     mMappedBuffer = nullptr;
     mGraphicBuffer->unlock();
   }
 }
 
-uint8_t*
-GrallocTextureClientOGL::GetBuffer() const
-{
-  MOZ_ASSERT(IsValid());
-  NS_WARN_IF_FALSE(mMappedBuffer, "Trying to get a gralloc buffer without getting the lock?");
-  return mMappedBuffer;
-}
-
 static gfx::SurfaceFormat
 SurfaceFormatForPixelFormat(android::PixelFormat aFormat)
 {
   switch (aFormat) {
   case PIXEL_FORMAT_RGBA_8888:
     return gfx::SurfaceFormat::R8G8B8A8;
   case PIXEL_FORMAT_BGRA_8888:
     return gfx::SurfaceFormat::B8G8R8A8;
@@ -374,32 +369,16 @@ GrallocTextureClientOGL::AllocateGralloc
 }
 
 bool
 GrallocTextureClientOGL::IsAllocated() const
 {
   return !!mGraphicBuffer.get();
 }
 
-bool
-GrallocTextureClientOGL::Allocate(uint32_t aSize)
-{
-  // see Bug 908196
-  MOZ_CRASH("This method should never be called.");
-  return false;
-}
-
-size_t
-GrallocTextureClientOGL::GetBufferSize() const
-{
-  // see Bug 908196
-  MOZ_CRASH("This method should never be called.");
-  return 0;
-}
-
 /*static*/ already_AddRefed<TextureClient>
 GrallocTextureClientOGL::FromSharedSurface(gl::SharedSurface* abstractSurf,
                                            TextureFlags flags)
 {
   auto surf = gl::SharedSurface_Gralloc::Cast(abstractSurf);
 
   RefPtr<TextureClient> ret = surf->GetTextureClient();
 
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -33,17 +33,17 @@ namespace layers {
  * and also use it directly as an OpenGL texture without the cost of texture
  * uploads.
  * Gralloc buffers can also be shared accros processes.
  *
  * More info about Gralloc here: https://wiki.mozilla.org/Platform/GFX/Gralloc
  *
  * This is only used in Firefox OS
  */
-class GrallocTextureClientOGL : public BufferTextureClient
+class GrallocTextureClientOGL : public TextureClient
 {
 public:
   GrallocTextureClientOGL(ISurfaceAllocator* aAllocator,
                           gfx::SurfaceFormat aFormat,
                           gfx::BackendType aMoz2dBackend,
                           TextureFlags aFlags = TextureFlags::DEFAULT);
 
   ~GrallocTextureClientOGL();
@@ -77,39 +77,33 @@ public:
     return mGraphicBuffer;
   }
 
   android::PixelFormat GetPixelFormat()
   {
     return mGraphicBuffer->getPixelFormat();
   }
 
-  virtual uint8_t* GetBuffer() const override;
-
   virtual gfx::DrawTarget* BorrowDrawTarget() override;
 
   virtual void UpdateFromSurface(gfx::SourceSurface* aSurface) override;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;
 
   virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
                                 gfx::IntSize aCbCrSize,
-                                StereoMode aStereoMode) override;
+                                StereoMode aStereoMode);
 
   bool AllocateForGLRendering(gfx::IntSize aSize);
 
   bool AllocateGralloc(gfx::IntSize aYSize, uint32_t aAndroidFormat, uint32_t aUsage);
 
   void SetIsOpaque(bool aIsOpaque) { mIsOpaque = aIsOpaque; }
 
-  virtual bool Allocate(uint32_t aSize) override;
-
-  virtual size_t GetBufferSize() const override;
-
   /**
    * Hold android::MediaBuffer.
    * MediaBuffer needs to be add refed to keep MediaBuffer alive
    * during TextureClient is in use.
    */
   void SetMediaBuffer(android::MediaBuffer* aMediaBuffer)
   {
     mMediaBuffer = aMediaBuffer;
@@ -123,16 +117,21 @@ public:
   virtual already_AddRefed<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
   static already_AddRefed<TextureClient> FromSharedSurface(gl::SharedSurface* surf,
                                                        TextureFlags flags);
 
 protected:
+  gfx::SurfaceFormat mFormat;
+  gfx::IntSize mSize;
+  gfx::BackendType mBackend;
+  OpenMode mOpenMode;
+
   /**
    * Unfortunately, until bug 879681 is fixed we need to use a GrallocBufferActor.
    */
   MaybeMagicGrallocBufferHandle mGrallocHandle;
 
   RefPtr<AsyncTransactionWaiter> mRemoveFromCompositableWaiter;
 
   android::sp<android::GraphicBuffer> mGraphicBuffer;
@@ -143,15 +142,16 @@ protected:
    */
   uint8_t* mMappedBuffer;
 
   RefPtr<gfx::DrawTarget> mDrawTarget;
 
   android::MediaBuffer* mMediaBuffer;
 
   bool mIsOpaque;
+  bool mLocked;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // MOZ_WIDGET_GONK
 #endif
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -5,16 +5,17 @@
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Tools.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/BufferTexture.h"
 #include "mozilla/RefPtr.h"
 #include "gfx2DGlue.h"
 #include "gfxImageSurface.h"
 #include "gfxTypes.h"
 #include "ImageContainer.h"
 #include "mozilla/layers/YCbCrImageDataSerializer.h"
 
 using namespace mozilla;
@@ -143,18 +144,16 @@ void AssertYCbCrSurfacesEqual(PlanarYCbC
   }
 }
 
 // Run the test for a texture client and a surface
 void TestTextureClientSurface(TextureClient* texture, gfxImageSurface* surface) {
 
   // client allocation
   ASSERT_TRUE(texture->CanExposeDrawTarget());
-  texture->AllocateForSurface(surface->GetSize());
-  ASSERT_TRUE(texture->IsAllocated());
 
   ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
   // client painting
   RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
   RefPtr<SourceSurface> source =
     gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
   dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
 
@@ -194,28 +193,18 @@ void TestTextureClientSurface(TextureCli
                           SurfaceFormatToImageFormat(hostDataSurface->GetFormat()));
     AssertSurfacesEqual(surface, hostSurface.get());
     host->Unlock();
   }
 }
 
 // Same as above, for YCbCr surfaces
 void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) {
-
-  // client allocation
-  ASSERT_TRUE(client->AsTextureClientYCbCr() != nullptr);
-  TextureClientYCbCr* texture = client->AsTextureClientYCbCr();
-  texture->AllocateForYCbCr(ycbcrData.mYSize,
-                            ycbcrData.mCbCrSize,
-                            ycbcrData.mStereoMode);
-  ASSERT_TRUE(client->IsAllocated());
-
-  ASSERT_TRUE(client->Lock(OpenMode::OPEN_READ_WRITE));
-  // client painting
-  texture->UpdateYCbCr(ycbcrData);
+  client->Lock(OpenMode::OPEN_READ_WRITE);
+  UpdateYCbCrTextureClient(client, ycbcrData);
   client->Unlock();
 
   // client serialization
   SurfaceDescriptor descriptor;
   ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor));
 
   ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t);
 
@@ -268,21 +257,25 @@ TEST(Layers, TextureSerialization) {
     gfxImageFormat::A8,
   };
 
   for (int f = 0; f < 3; ++f) {
     RefPtr<gfxImageSurface> surface = new gfxImageSurface(IntSize(400,300), formats[f]);
     SetupSurface(surface.get());
     AssertSurfacesEqual(surface, surface);
 
-    RefPtr<TextureClient> client
-      = new MemoryTextureClient(nullptr,
-                                mozilla::gfx::ImageFormatToSurfaceFormat(surface->Format()),
-                                gfx::BackendType::CAIRO,
-                                TextureFlags::DEALLOCATE_CLIENT);
+    auto texData = BufferTextureData::Create(surface->GetSize(),
+      gfx::ImageFormatToSurfaceFormat(surface->Format()),
+      gfx::BackendType::CAIRO, TextureFlags::DEALLOCATE_CLIENT, ALLOC_DEFAULT, nullptr
+    );
+    ASSERT_TRUE(!!texData);
+
+    RefPtr<TextureClient> client = new ClientTexture(
+      texData, TextureFlags::DEALLOCATE_CLIENT, nullptr
+    );
 
     TestTextureClientSurface(client, surface);
 
     // XXX - Test more texture client types.
   }
 }
 
 TEST(Layers, TextureYCbCrSerialization) {
@@ -305,18 +298,15 @@ TEST(Layers, TextureYCbCrSerialization) 
   clientData.mStereoMode = StereoMode::MONO;
   clientData.mYSkip = 0;
   clientData.mCbSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mCrSkip = 0;
   clientData.mPicX = 0;
   clientData.mPicX = 0;
 
-  RefPtr<TextureClient> client
-    = new MemoryTextureClient(nullptr,
-                              mozilla::gfx::SurfaceFormat::YUV,
-                              gfx::BackendType::CAIRO,
-                              TextureFlags::DEALLOCATE_CLIENT);
+  RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(nullptr, clientData.mYSize, clientData.mCbCrSize,
+                                                               StereoMode::MONO, TextureFlags::DEALLOCATE_CLIENT);
 
   TestTextureClientYCbCr(client, clientData);
 
   // XXX - Test more texture client types.
 }