Bug 1250500 - Avoid copying BufferTextureHost when possible. r=sotaro
authorNicolas Silva <nsilva@mozilla.com>
Thu, 25 Feb 2016 14:15:52 +0100
changeset 321907 52e355f34371182f6011a05da6a6d3ccecd03c77
parent 321906 e6d0c56847ced14f0b7500b12a852defd83d5a7a
child 321908 aa348994df48dd1ab2216b9f13473c6a6049ec9f
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro
bugs1250500
milestone47.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 1250500 - Avoid copying BufferTextureHost when possible. r=sotaro
gfx/layers/BufferTexture.cpp
gfx/layers/BufferTexture.h
gfx/layers/Compositor.h
gfx/layers/basic/BasicCompositor.cpp
gfx/layers/basic/BasicCompositor.h
gfx/layers/basic/X11BasicCompositor.h
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TextureHost.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/ISurfaceAllocator.cpp
gfx/layers/ipc/ISurfaceAllocator.h
gfx/layers/ipc/LayersSurfaces.ipdlh
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -1,20 +1,25 @@
 /* -*- 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/layers/CompositableForwarder.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/fallible.h"
 
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
 namespace mozilla {
 namespace layers {
 
 class MemoryTextureData : public BufferTextureData
 {
 public:
   static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                                    gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
@@ -78,26 +83,46 @@ public:
   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;
 };
 
+static bool UsingX11Compositor()
+{
+#ifdef MOZ_WIDGET_GTK
+  return gfxPlatformGtk::GetPlatform()->UseXRender();
+#endif
+  return false;
+}
+
+static bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+                                         LayersBackend aLayersBackend)
+{
+  return aLayersBackend != LayersBackend::LAYERS_BASIC
+      || UsingX11Compositor()
+      || aFormat == gfx::SurfaceFormat::UNKNOWN
+      || aFormat == gfx::SurfaceFormat::YUV
+      || aFormat == gfx::SurfaceFormat::NV12;
+}
+
 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);
+    return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+                                     aAllocFlags, aAllocator);
   } else {
-    return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+    return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+                                    aAllocFlags, aAllocator);
   }
 }
 
 BufferTextureData*
 BufferTextureData::CreateInternal(ISurfaceAllocator* aAllocator,
                                   const BufferDescriptor& aDesc,
                                   gfx::BackendType aMoz2DBackend,
                                   int32_t aBufferSize,
@@ -174,16 +199,26 @@ BufferTextureData::GetSize() const
 }
 
 gfx::SurfaceFormat
 BufferTextureData::GetFormat() const
 {
   return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
 }
 
+
+bool
+BufferTextureData::HasInternalBuffer() const
+{
+  if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
+    return true;
+  }
+  return mDescriptor.get_RGBDescriptor().hasInternalBuffer();
+}
+
 bool
 BufferTextureData::SupportsMoz2D() const
 {
   switch (GetFormat()) {
     case gfx::SurfaceFormat::YUV:
     case gfx::SurfaceFormat::NV12:
     case gfx::SurfaceFormat::UNKNOWN:
       return false;
@@ -380,17 +415,17 @@ static bool InitBuffer(uint8_t* buf, siz
 
   return true;
 }
 
 MemoryTextureData*
 MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
                           gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
                           TextureAllocationFlags aAllocFlags,
-                          ISurfaceAllocator*)
+                          ISurfaceAllocator* aAllocator)
 {
   // Should have used CreateForYCbCr.
   MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
 
   if (aSize.width <= 0 || aSize.height <= 0) {
     gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
     return nullptr;
   }
@@ -400,19 +435,24 @@ MemoryTextureData::Create(gfx::IntSize a
     return nullptr;
   }
 
   uint8_t* buf = new (fallible) uint8_t[bufSize];
   if (!InitBuffer(buf, bufSize, aAllocFlags)) {
     return nullptr;
   }
 
+  auto fwd = aAllocator ? aAllocator->AsCompositableForwarder() : nullptr;
+  bool hasInternalBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+                                              fwd->GetCompositorBackendType())
+                               : true;
+
   GfxMemoryImageReporter::DidAlloc(buf);
 
-  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasInternalBuffer);
 
   return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
 }
 
 void
 MemoryTextureData::Deallocate(ISurfaceAllocator*)
 {
   MOZ_ASSERT(mBuffer);
@@ -472,17 +512,22 @@ ShmemTextureData::Create(gfx::IntSize aS
     return nullptr;
   }
 
   uint8_t* buf = shm.get<uint8_t>();
   if (!InitBuffer(buf, bufSize, aAllocFlags)) {
     return nullptr;
   }
 
-  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+  auto fwd = aAllocator->AsCompositableForwarder();
+  bool hasInternalBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+                                              fwd->GetCompositorBackendType())
+                               : true;
+
+  BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasInternalBuffer);
 
   return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
 
   return nullptr;
 }
 
 TextureData*
 ShmemTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -50,17 +50,17 @@ public:
   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; }
+  virtual bool HasInternalBuffer() const override;
 
   // use TextureClient's default implementation
   virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
 
   // Don't use this.
   void SetDesciptor(const BufferDescriptor& aDesc);
 
 protected:
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -107,16 +107,17 @@
  */
 
 class nsIWidget;
 
 namespace mozilla {
 namespace gfx {
 class Matrix;
 class DrawTarget;
+class DataSourceSurface;
 } // namespace gfx
 
 namespace layers {
 
 struct Effect;
 struct EffectChain;
 class Image;
 class ImageHostOverlay;
@@ -187,16 +188,20 @@ public:
     : mCompositorID(0)
     , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
     , mParent(aParent)
     , mScreenRotation(ROTATION_0)
   {
   }
 
   virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
+
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
+
   virtual bool Initialize() = 0;
   virtual void Destroy() = 0;
 
   /**
    * Return true if the effect type is supported.
    *
    * By default Compositor implementations should support all effects but in
    * some rare cases it is not possible to support an effect efficiently.
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -31,16 +31,28 @@ using namespace mozilla::gfx;
 namespace layers {
 
 class DataTextureSourceBasic : public DataTextureSource
                              , public TextureSourceBasic
 {
 public:
   virtual const char* Name() const override { return "DataTextureSourceBasic"; }
 
+  explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+  : mSurface(aSurface)
+  , mWrappingExistingData(!!aSurface)
+  {}
+
+  virtual DataTextureSource* AsDataTextureSource() override
+  {
+    // If the texture wraps someone else's memory we'd rather not use it as
+    // a DataTextureSource per say (that is call Update on it).
+    return mWrappingExistingData ? nullptr : this;
+  }
+
   virtual TextureSourceBasic* AsSourceBasic() override { return this; }
 
   virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
 
   SurfaceFormat GetFormat() const override
   {
     return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
   }
@@ -49,28 +61,33 @@ public:
   {
     return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
   }
 
   virtual bool Update(gfx::DataSourceSurface* aSurface,
                       nsIntRegion* aDestRegion = nullptr,
                       gfx::IntPoint* aSrcOffset = nullptr) override
   {
+    MOZ_ASSERT(!mWrappingExistingData);
+    if (mWrappingExistingData) {
+      return false;
+    }
     mSurface = aSurface;
     return true;
   }
 
   virtual void DeallocateDeviceData() override
   {
     mSurface = nullptr;
     SetUpdateSerial(0);
   }
 
 public:
   RefPtr<gfx::DataSourceSurface> mSurface;
+  bool mWrappingExistingData;
 };
 
 BasicCompositor::BasicCompositor(nsIWidget *aWidget)
   : mWidget(aWidget)
   , mDidExternalComposition(false)
 {
   MOZ_COUNT_CTOR(BasicCompositor);
 
@@ -177,17 +194,24 @@ BasicCompositor::CreateRenderTargetForWi
   }
 
   return rt.forget();
 }
 
 already_AddRefed<DataTextureSource>
 BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
 {
-  RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
+  RefPtr<DataTextureSource> result = new DataTextureSourceBasic(nullptr);
+  return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+  RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
   return result.forget();
 }
 
 bool
 BasicCompositor::SupportsEffect(EffectTypes aEffect)
 {
   return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
 }
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -65,16 +65,19 @@ public:
   virtual already_AddRefed<CompositingRenderTarget>
   CreateRenderTargetForWindow(const gfx::IntRect& aRect,
                               SurfaceInitMode aInit,
                               BufferMode aBufferMode);
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
 
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
   virtual bool SupportsEffect(EffectTypes aEffect) override;
 
   virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
   {
     mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
     mRenderTarget->BindRenderTarget();
   }
   virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -47,15 +47,18 @@ class X11BasicCompositor : public BasicC
 {
 public:
 
   explicit X11BasicCompositor(nsIWidget *aWidget) : BasicCompositor(aWidget) {}
 
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
 
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
   virtual void EndFrame() override;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -390,22 +390,24 @@ BufferTextureHost::BufferTextureHost(con
 , mNeedsFullUpdate(false)
 {
   mDescriptor = aDesc;
   switch (mDescriptor.type()) {
     case BufferDescriptor::TYCbCrDescriptor: {
       const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
       mSize = ycbcr.ySize();
       mFormat = gfx::SurfaceFormat::YUV;
+      mHasInternalBuffer = true;
       break;
     }
     case BufferDescriptor::TRGBDescriptor: {
       const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
       mSize = rgb.size();
       mFormat = rgb.format();
+      mHasInternalBuffer = rgb.hasInternalBuffer();
       break;
     }
     default: MOZ_CRASH();
   }
   if (aFlags & TextureFlags::COMPONENT_ALPHA) {
     // One texture of a component alpha texture pair will start out all white.
     // This hack allows us to easily make sure that white will be uploaded.
     // See bug 1138934
@@ -486,19 +488,52 @@ BufferTextureHost::Lock()
 
 void
 BufferTextureHost::Unlock()
 {
   MOZ_ASSERT(mLocked);
   mLocked = false;
 }
 
+bool
+BufferTextureHost::EnsureWrappingTextureSource()
+{
+  MOZ_ASSERT(!mHasInternalBuffer);
+  MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV);
+
+  if (mFirstSource) {
+    return true;
+  }
+
+  if (!mCompositor) {
+    return false;
+  }
+
+  RefPtr<gfx::DataSourceSurface> surf =
+    gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+      ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+
+  if (!surf) {
+    return false;
+  }
+
+  mFirstSource = mCompositor->CreateDataTextureSourceAround(surf);
+  mFirstSource->SetUpdateSerial(mUpdateSerial);
+  mFirstSource->SetOwner(this);
+
+  return true;
+}
+
 void
 BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
 {
+  if (!mHasInternalBuffer) {
+    EnsureWrappingTextureSource();
+  }
+
   if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
     // We are already attached to a TextureSource, nothing to do except tell
     // the compositable to use it.
     aTexture = mFirstSource.get();
     return;
   }
 
   // We don't own it, apparently.
@@ -589,16 +624,20 @@ BufferTextureHost::Upload(nsIntRegion *a
     // deserializing it.
     return false;
   }
   if (!mCompositor) {
     // This can happen if we send textures to a compositable that isn't yet
     // attached to a layer.
     return false;
   }
+  if (!mHasInternalBuffer && EnsureWrappingTextureSource()) {
+    return true;
+  }
+
   if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
     NS_WARNING("BufferTextureHost: unsupported format!");
     return false;
   } else if (mFormat == gfx::SurfaceFormat::YUV) {
     const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
 
     if (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
       RefPtr<gfx::DataSourceSurface> surf =
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -625,33 +625,35 @@ public:
    * GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
    */
   virtual gfx::SurfaceFormat GetFormat() const override;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
-  virtual bool HasInternalBuffer() const override { return true; }
+  virtual bool HasInternalBuffer() const override { return mHasInternalBuffer; }
 
 protected:
   bool Upload(nsIntRegion *aRegion = nullptr);
   bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+  bool EnsureWrappingTextureSource();
 
   virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
 
   BufferDescriptor mDescriptor;
   RefPtr<Compositor> mCompositor;
   RefPtr<DataTextureSource> mFirstSource;
   nsIntRegion mMaybeUpdatedRegion;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   uint32_t mUpdateSerial;
   bool mLocked;
   bool mNeedsFullUpdate;
+  bool mHasInternalBuffer;
 };
 
 /**
  * TextureHost that wraps shared memory.
  * the corresponding texture on the client side is ShmemTextureClient.
  * This TextureHost is backend-independent.
  */
 class ShmemTextureHost : public BufferTextureHost
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -167,16 +167,18 @@ public:
   {
     return mTextureFactoryIdentifier;
   }
 
   int32_t GetSerial() { return mSerial; }
 
   SyncObject* GetSyncObject() { return mSyncObject; }
 
+  virtual CompositableForwarder* AsCompositableForwarder() override { return this; }
+
 protected:
   TextureFactoryIdentifier mTextureFactoryIdentifier;
   nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
   nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
   RefPtr<SyncObject> mSyncObject;
   const int32_t mSerial;
   static mozilla::Atomic<int32_t> sSerialCounter;
 };
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -142,17 +142,21 @@ ISurfaceAllocator::AllocSurfaceDescripto
     mozilla::ipc::Shmem shmem;
     if (!AllocUnsafeShmem(size, shmemType, &shmem)) {
       return false;
     }
 
     bufferDesc = shmem;
   }
 
-  *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format), bufferDesc);
+  // Use an intermediate buffer by default. Skipping the intermediate buffer is
+  // only possible in certain configurations so let's keep it simple here for now.
+  const bool hasIntermediateBuffer = true;
+  *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format, hasIntermediateBuffer),
+                                     bufferDesc);
 
   return true;
 }
 
 /* static */ bool
 ISurfaceAllocator::IsShmem(SurfaceDescriptor* aSurface)
 {
   return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -36,16 +36,17 @@ class Shmem;
 } // namespace ipc
 namespace gfx {
 class DataSourceSurface;
 } // namespace gfx
 
 namespace layers {
 
 class MaybeMagicGrallocBufferHandle;
+class CompositableForwarder;
 
 enum BufferCapabilities {
   DEFAULT_BUFFER_CAPS = 0,
   /**
    * The allocated buffer must be efficiently mappable as a DataSourceSurface.
    */
   MAP_AS_IMAGE_SURFACE = 1 << 0,
   /**
@@ -155,16 +156,17 @@ public:
   virtual MessageLoop * GetMessageLoop() const
   {
     return mDefaultMessageLoop;
   }
 
   // Returns true if aSurface wraps a Shmem.
   static bool IsShmem(SurfaceDescriptor* aSurface);
 
+  virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
 protected:
 
   virtual bool IsOnCompositorSide() const = 0;
 
   virtual ~ISurfaceAllocator();
 
   void ShrinkShmemSectionHeap();
 
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -96,16 +96,17 @@ struct SurfaceDescriptorSharedGLTexture 
 struct SurfaceDescriptorGralloc {
   MaybeMagicGrallocBufferHandle buffer;
   bool isOpaque;
 };
 
 struct RGBDescriptor {
   IntSize size;
   SurfaceFormat format;
+  bool hasInternalBuffer;
 };
 
 struct YCbCrDescriptor {
   IntSize ySize;
   IntSize cbCrSize;
   uint32_t yOffset;
   uint32_t cbOffset;
   uint32_t crOffset;