Bug 1219210 - Add ITextureClientAllocationHelper and VENUS color handling r=nical,jolin
authorSotaro Ikeda <sotaro.ikeda.g@gmail.com>
Fri, 04 Dec 2015 02:13:49 -0800
changeset 275622 6212d0b0e634d0f491ff2baaf7ab465dc2b51fe4
parent 275621 ca6084eaafbfb041a9bc081228cdb8c7e879eb38
child 275623 32065c89200f702e9d83d1e88900d57d24f2eb4a
push id68894
push usersikeda@mozilla.com
push dateFri, 04 Dec 2015 10:13:57 +0000
treeherdermozilla-inbound@6212d0b0e634 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical, jolin
bugs1219210
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 1219210 - Add ITextureClientAllocationHelper and VENUS color handling r=nical,jolin
dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
gfx/layers/client/TextureClientRecycleAllocator.cpp
gfx/layers/client/TextureClientRecycleAllocator.h
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -36,16 +36,69 @@
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 using namespace mozilla::layers;
 using namespace android;
 typedef android::MediaCodecProxy MediaCodecProxy;
 
 namespace mozilla {
 
+class GonkTextureClientAllocationHelper : public layers::ITextureClientAllocationHelper
+{
+public:
+  GonkTextureClientAllocationHelper(uint32_t aGrallocFormat,
+                                    gfx::IntSize aSize)
+    : ITextureClientAllocationHelper(gfx::SurfaceFormat::UNKNOWN,
+                                     aSize,
+                                     BackendSelector::Content,
+                                     TextureFlags::DEALLOCATE_CLIENT,
+                                     ALLOC_DISALLOW_BUFFERTEXTURECLIENT)
+    , mGrallocFormat(aGrallocFormat)
+  {}
+
+  already_AddRefed<TextureClient> Allocate(CompositableForwarder* aAllocator) override
+  {
+    uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+                     android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                     android::GraphicBuffer::USAGE_HW_TEXTURE;
+
+    GrallocTextureData* texData = GrallocTextureData::Create(mSize, mGrallocFormat,
+                                                             gfx::BackendType::NONE,
+                                                             usage, aAllocator);
+    if (!texData) {
+      return nullptr;
+    }
+    sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
+    if (!graphicBuffer.get()) {
+      return nullptr;
+    }
+    RefPtr<TextureClient> textureClient =
+      TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator);
+    return textureClient.forget();
+  }
+
+  bool IsCompatible(TextureClient* aTextureClient) override
+  {
+    if (!aTextureClient) {
+      return false;
+    }
+    sp<GraphicBuffer> graphicBuffer =
+      static_cast<GrallocTextureData*>(aTextureClient->GetInternalData())->GetGraphicBuffer();
+    if (!graphicBuffer.get() ||
+        static_cast<uint32_t>(graphicBuffer->getPixelFormat()) != mGrallocFormat ||
+        aTextureClient->GetSize() != mSize) {
+      return false;
+    }
+    return true;
+  }
+
+private:
+  uint32_t mGrallocFormat;
+};
+
 GonkVideoDecoderManager::GonkVideoDecoderManager(
   mozilla::layers::ImageContainer* aImageContainer,
   const VideoInfo& aConfig)
   : mImageContainer(aImageContainer)
   , mColorConverterBufferSize(0)
   , mPendingReleaseItemsLock("GonkVideoDecoderManager::mPendingReleaseItemsLock")
   , mNeedsCopyBuffer(false)
 {
@@ -264,84 +317,105 @@ CopyYUV(PlanarYCbCrData& aSource, Planar
 }
 
 inline static int
 Align(int aX, int aAlign)
 {
   return (aX + aAlign - 1) & ~(aAlign - 1);
 }
 
+// Venus formats are doucmented in kernel/include/media/msm_media_info.h:
+// * Y_Stride : Width aligned to 128
+// * UV_Stride : Width aligned to 128
+// * Y_Scanlines: Height aligned to 32
+// * UV_Scanlines: Height/2 aligned to 16
+// * Total size = align((Y_Stride * Y_Scanlines
+// *          + UV_Stride * UV_Scanlines + 4096), 4096)
+static void
+CopyVenus(uint8_t* aSrc, uint8_t* aDest, uint32_t aWidth, uint32_t aHeight)
+{
+  size_t yStride = Align(aWidth, 128);
+  uint8_t* s = aSrc;
+  uint8_t* d = aDest;
+  for (size_t i = 0; i < aHeight; i++) {
+    memcpy(d, s, aWidth);
+    s += yStride;
+    d += yStride;
+  }
+  size_t uvStride = yStride;
+  size_t uvLines = (aHeight + 1) / 2;
+  size_t ySize = yStride * Align(aHeight, 32);
+  s = aSrc + ySize;
+  d = aDest + ySize;
+  for (size_t i = 0; i < uvLines; i++) {
+    memcpy(d, s, aWidth);
+    s += uvStride;
+    d += uvStride;
+  }
+}
+
 static void
 CopyGraphicBuffer(sp<GraphicBuffer>& aSource, sp<GraphicBuffer>& aDestination)
 {
   void* srcPtr = nullptr;
   aSource->lock(GraphicBuffer::USAGE_SW_READ_OFTEN, &srcPtr);
   void* destPtr = nullptr;
   aDestination->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destPtr);
   MOZ_ASSERT(srcPtr && destPtr);
 
   // Build PlanarYCbCrData for source buffer.
   PlanarYCbCrData srcData;
   switch (aSource->getPixelFormat()) {
-    case HAL_PIXEL_FORMAT_YV12:
+    case HAL_PIXEL_FORMAT_YV12: {
       // Android YV12 format is defined in system/core/include/system/graphics.h
       srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
       srcData.mYSkip = 0;
       srcData.mYSize.width = aSource->getWidth();
       srcData.mYSize.height = aSource->getHeight();
       srcData.mYStride = aSource->getStride();
       // 4:2:0.
       srcData.mCbCrSize.width = srcData.mYSize.width / 2;
       srcData.mCbCrSize.height = srcData.mYSize.height / 2;
       srcData.mCrChannel = srcData.mYChannel + (srcData.mYStride * srcData.mYSize.height);
       // Aligned to 16 bytes boundary.
       srcData.mCbCrStride = Align(srcData.mYStride / 2, 16);
       srcData.mCrSkip = 0;
       srcData.mCbChannel = srcData.mCrChannel + (srcData.mCbCrStride * srcData.mCbCrSize.height);
       srcData.mCbSkip = 0;
+
+      // Build PlanarYCbCrData for destination buffer.
+      PlanarYCbCrData destData;
+      destData.mYChannel = static_cast<uint8_t*>(destPtr);
+      destData.mYSkip = 0;
+      destData.mYSize.width = aDestination->getWidth();
+      destData.mYSize.height = aDestination->getHeight();
+      destData.mYStride = aDestination->getStride();
+      // 4:2:0.
+      destData.mCbCrSize.width = destData.mYSize.width / 2;
+      destData.mCbCrSize.height = destData.mYSize.height / 2;
+      destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height);
+      // Aligned to 16 bytes boundary.
+      destData.mCbCrStride = Align(destData.mYStride / 2, 16);
+      destData.mCrSkip = 0;
+      destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height);
+      destData.mCbSkip = 0;
+
+      CopyYUV(srcData, destData);
       break;
+    }
     case GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS:
-      // Venus formats are doucmented in kernel/include/media/msm_media_info.h:
-      srcData.mYChannel = static_cast<uint8_t*>(srcPtr);
-      srcData.mYSkip = 0;
-      srcData.mYSize.width = aSource->getWidth();
-      srcData.mYSize.height = aSource->getHeight();
-      // - Y & UV Width aligned to 128
-      srcData.mYStride = aSource->getStride();
-      srcData.mCbCrSize.width = (srcData.mYSize.width + 1) / 2;
-      srcData.mCbCrSize.height = (srcData.mYSize.height + 1) / 2;
-      // - Y height aligned to 32
-      srcData.mCbChannel = srcData.mYChannel + (srcData.mYStride * Align(srcData.mYSize.height, 32));
-      // Interleaved VU plane.
-      srcData.mCbSkip = 1;
-      srcData.mCrChannel = srcData.mCbChannel + 1;
-      srcData.mCrSkip = 1;
-      srcData.mCbCrStride = srcData.mYStride;
+      CopyVenus(static_cast<uint8_t*>(srcPtr),
+                static_cast<uint8_t*>(destPtr),
+                aSource->getWidth(),
+                aSource->getHeight());
       break;
     default:
       NS_ERROR("Unsupported input gralloc image type. Should never be here.");
   }
-  // Build PlanarYCbCrData for destination buffer.
-  PlanarYCbCrData destData;
-  destData.mYChannel = static_cast<uint8_t*>(destPtr);
-  destData.mYSkip = 0;
-  destData.mYSize.width = aDestination->getWidth();
-  destData.mYSize.height = aDestination->getHeight();
-  destData.mYStride = aDestination->getStride();
-  // 4:2:0.
-  destData.mCbCrSize.width = destData.mYSize.width / 2;
-  destData.mCbCrSize.height = destData.mYSize.height / 2;
-  destData.mCrChannel = destData.mYChannel + (destData.mYStride * destData.mYSize.height);
-  // Aligned to 16 bytes boundary.
-  destData.mCbCrStride = Align(destData.mYStride / 2, 16);
-  destData.mCrSkip = 0;
-  destData.mCbChannel = destData.mCrChannel + (destData.mCbCrStride * destData.mCbCrSize.height);
-  destData.mCbSkip = 0;
 
-  CopyYUV(srcData, destData);
 
   aSource->unlock();
   aDestination->unlock();
 }
 
 already_AddRefed<VideoData>
 GonkVideoDecoderManager::CreateVideoDataFromGraphicBuffer(MediaBuffer* aSource,
                                                           gfx::IntRect& aPicture)
@@ -354,29 +428,23 @@ GonkVideoDecoderManager::CreateVideoData
     if (!mCopyAllocator) {
       mCopyAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
     }
     if (!mCopyAllocator) {
       GVDM_LOG("Create buffer allocator failed!");
       return nullptr;
     }
 
-    gfx::IntSize size(Align(aPicture.width, 2) , Align(aPicture.height, 2));
-    textureClient =
-      mCopyAllocator->CreateOrRecycle(gfx::SurfaceFormat::YUV, size,
-                                      BackendSelector::Content,
-                                      TextureFlags::DEFAULT,
-                                      ALLOC_DISALLOW_BUFFERTEXTURECLIENT);
+    gfx::IntSize size(srcBuffer->getWidth(), srcBuffer->getHeight());
+    GonkTextureClientAllocationHelper helper(srcBuffer->getPixelFormat(), size);
+    textureClient = mCopyAllocator->CreateOrRecycle(helper);
     if (!textureClient) {
       GVDM_LOG("Copy buffer allocation failed!");
       return nullptr;
     }
-    // Update size to match buffer's.
-    aPicture.width = size.width;
-    aPicture.height = size.height;
 
     sp<GraphicBuffer> destBuffer =
       static_cast<GrallocTextureData*>(textureClient->GetInternalData())->GetGraphicBuffer();
 
     CopyGraphicBuffer(srcBuffer, destBuffer);
   } else {
     textureClient = mNativeWindow->getTextureClientFromBuffer(srcBuffer.get());
     textureClient->SetRecycleCallback(GonkVideoDecoderManager::RecycleCallback, this);
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -27,16 +27,55 @@ public:
     return mTextureClient;
   }
 
   void ClearTextureClient() { mTextureClient = nullptr; }
 protected:
   RefPtr<TextureClient> mTextureClient;
 };
 
+class DefaultTextureClientAllocationHelper : public ITextureClientAllocationHelper
+{
+public:
+  DefaultTextureClientAllocationHelper(TextureClientRecycleAllocator* aAllocator,
+                                       gfx::SurfaceFormat aFormat,
+                                       gfx::IntSize aSize,
+                                       BackendSelector aSelector,
+                                       TextureFlags aTextureFlags,
+                                       TextureAllocationFlags aAllocationFlags)
+    : ITextureClientAllocationHelper(aFormat,
+                                     aSize,
+                                     aSelector,
+                                     aTextureFlags,
+                                     aAllocationFlags)
+    , mAllocator(aAllocator)
+  {}
+
+  bool IsCompatible(TextureClient* aTextureClient) override
+  {
+    if (aTextureClient->GetFormat() != mFormat ||
+        aTextureClient->GetSize() != mSize) {
+      return false;
+    }
+    return true;
+  }
+
+  already_AddRefed<TextureClient> Allocate(CompositableForwarder* aAllocator) override
+  {
+    return mAllocator->Allocate(mFormat,
+                                mSize,
+                                mSelector,
+                                mTextureFlags,
+                                mAllocationFlags);
+  }
+
+protected:
+  TextureClientRecycleAllocator* mAllocator;
+};
+
 TextureClientRecycleAllocator::TextureClientRecycleAllocator(CompositableForwarder* aAllocator)
   : mSurfaceAllocator(aAllocator)
   , mMaxPooledSize(kMaxPooledSized)
   , mLock("TextureClientRecycleAllocatorImp.mLock")
 {
 }
 
 TextureClientRecycleAllocator::~TextureClientRecycleAllocator()
@@ -74,49 +113,60 @@ private:
 
 already_AddRefed<TextureClient>
 TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
                                                gfx::IntSize aSize,
                                                BackendSelector aSelector,
                                                TextureFlags aTextureFlags,
                                                TextureAllocationFlags aAllocFlags)
 {
+  MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
+  DefaultTextureClientAllocationHelper helper(this,
+                                              aFormat,
+                                              aSize,
+                                              aSelector,
+                                              aTextureFlags,
+                                              aAllocFlags);
+  return CreateOrRecycle(helper);
+}
+
+already_AddRefed<TextureClient>
+TextureClientRecycleAllocator::CreateOrRecycle(ITextureClientAllocationHelper& aHelper)
+{
   // TextureAllocationFlags is actually used only by ContentClient.
   // This class does not handle ContentClient's TextureClient allocation.
-  MOZ_ASSERT(aAllocFlags == TextureAllocationFlags::ALLOC_DEFAULT ||
-             aAllocFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT ||
-             aAllocFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT);
-  MOZ_ASSERT(!(aTextureFlags & TextureFlags::RECYCLE));
-  aTextureFlags = aTextureFlags | TextureFlags::RECYCLE; // Set recycle flag
+  MOZ_ASSERT(aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DEFAULT ||
+             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_DISALLOW_BUFFERTEXTURECLIENT ||
+             aHelper.mAllocationFlags == TextureAllocationFlags::ALLOC_FOR_OUT_OF_BAND_CONTENT);
+  MOZ_ASSERT(aHelper.mTextureFlags & TextureFlags::RECYCLE);
 
   RefPtr<TextureClientHolder> textureHolder;
 
   {
     MutexAutoLock lock(mLock);
     if (!mPooledClients.empty()) {
       textureHolder = mPooledClients.top();
       mPooledClients.pop();
       Task* task = nullptr;
       // If a pooled TextureClient is not compatible, release it.
-      if (textureHolder->GetTextureClient()->GetFormat() != aFormat ||
-          textureHolder->GetTextureClient()->GetSize() != aSize) {
+      if (!aHelper.IsCompatible(textureHolder->GetTextureClient())) {
         // Release TextureClient.
         task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
         textureHolder->ClearTextureClient();
         textureHolder = nullptr;
       } else {
-        task = new TextureClientRecycleTask(textureHolder->GetTextureClient(), aTextureFlags);
+        task = new TextureClientRecycleTask(textureHolder->GetTextureClient(), aHelper.mTextureFlags);
       }
       mSurfaceAllocator->GetMessageLoop()->PostTask(FROM_HERE, task);
     }
   }
 
   if (!textureHolder) {
     // Allocate new TextureClient
-    RefPtr<TextureClient> texture = Allocate(aFormat, aSize, aSelector, aTextureFlags, aAllocFlags);
+    RefPtr<TextureClient> texture = aHelper.Allocate(mSurfaceAllocator);
     if (!texture) {
       return nullptr;
     }
     textureHolder = new TextureClientHolder(texture);
   }
 
   {
     MutexAutoLock lock(mLock);
--- a/gfx/layers/client/TextureClientRecycleAllocator.h
+++ b/gfx/layers/client/TextureClientRecycleAllocator.h
@@ -11,19 +11,42 @@
 #include "mozilla/gfx/Types.h"
 #include "mozilla/RefPtr.h"
 #include "TextureClient.h"
 #include "mozilla/Mutex.h"
 
 namespace mozilla {
 namespace layers {
 
-class ISurfaceAllocator;
 class TextureClientHolder;
 
+class ITextureClientAllocationHelper
+{
+public:
+  ITextureClientAllocationHelper(gfx::SurfaceFormat aFormat,
+                                 gfx::IntSize aSize,
+                                 BackendSelector aSelector,
+                                 TextureFlags aTextureFlags,
+                                 TextureAllocationFlags aAllocationFlags)
+    : mFormat(aFormat)
+    , mSize(aSize)
+    , mSelector(aSelector)
+    , mTextureFlags(aTextureFlags | TextureFlags::RECYCLE) // Set recycle flag
+    , mAllocationFlags(aAllocationFlags)
+  {}
+
+  virtual already_AddRefed<TextureClient> Allocate(CompositableForwarder* aAllocator) = 0;
+  virtual bool IsCompatible(TextureClient* aTextureClient) = 0;
+
+  const gfx::SurfaceFormat mFormat;
+  const gfx::IntSize mSize;
+  const BackendSelector mSelector;
+  const TextureFlags mTextureFlags;
+  const TextureAllocationFlags mAllocationFlags;
+};
 
 /**
  * TextureClientRecycleAllocator provides TextureClients allocation and
  * recycling capabilities. It expects allocations of same sizes and
  * attributres. If a recycled TextureClient is different from
  * requested one, the recycled one is dropped and new TextureClient is allocated.
  *
  * By default this uses TextureClient::CreateForDrawing to allocate new texture
@@ -44,28 +67,32 @@ public:
   // Creates and allocates a TextureClient.
   already_AddRefed<TextureClient>
   CreateOrRecycle(gfx::SurfaceFormat aFormat,
                   gfx::IntSize aSize,
                   BackendSelector aSelector,
                   TextureFlags aTextureFlags,
                   TextureAllocationFlags flags = ALLOC_DEFAULT);
 
+  already_AddRefed<TextureClient>
+  CreateOrRecycle(ITextureClientAllocationHelper& aHelper);
+
 protected:
   virtual already_AddRefed<TextureClient>
   Allocate(gfx::SurfaceFormat aFormat,
            gfx::IntSize aSize,
            BackendSelector aSelector,
            TextureFlags aTextureFlags,
            TextureAllocationFlags aAllocFlags);
 
   RefPtr<CompositableForwarder> mSurfaceAllocator;
 
 private:
   friend class TextureClient;
+  friend class DefaultTextureClientAllocationHelper;
   void RecycleTextureClient(TextureClient* aClient);
 
   static const uint32_t kMaxPooledSized = 2;
   uint32_t mMaxPooledSize;
 
   std::map<TextureClient*, RefPtr<TextureClientHolder> > mInUseClients;
 
   // On b2g gonk, std::queue might be a better choice.