Bug 1250500 - Avoid copying BufferTextureHost when possible. r=sotaro
authorNicolas Silva <nsilva@mozilla.com>
Thu, 25 Feb 2016 14:15:52 +0100
changeset 285595 52e355f34371182f6011a05da6a6d3ccecd03c77
parent 285594 e6d0c56847ced14f0b7500b12a852defd83d5a7a
child 285596 aa348994df48dd1ab2216b9f13473c6a6049ec9f
push id17831
push userkwierso@gmail.com
push dateThu, 25 Feb 2016 22:42:17 +0000
treeherderfx-team@8e34b12969bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssotaro
bugs1250500
milestone47.0a1
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;