Bug 1200595 - Buffer TextureData implementation. r=mattwoodrow
☠☠ backed out by 027edbd76ba2 ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Thu, 15 Oct 2015 17:53:33 +0200
changeset 273540 6dc38a1e6073a37e27b34c81c81f8bdca15e8e48
parent 273539 fe2164aa146845859dcf9945d7da7eef5d35fd20
child 273541 0092c9d7a86b907089be8961e947b6a31e5f6cfa
push id29705
push userkwierso@gmail.com
push dateFri, 20 Nov 2015 22:36:48 +0000
treeherdermozilla-central@e3d9cf3dc326 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1200595
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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.
 }