Bug 981315: Add ShmemSection and use it for gfxShmSharedReacLock. r=gal
authorBas Schouten <bschouten@mozilla.com>
Thu, 13 Mar 2014 06:17:05 +0100
changeset 190505 193df828b5a2c929784b972183fe134e958d3bfc
parent 190504 ad954f1cf3db3b89455265e45ffe890a3b9a59f8
child 190506 e30ce0237dac19f913fe4127e2ef57da151d657e
push idunknown
push userunknown
push dateunknown
reviewersgal
bugs981315
milestone30.0a1
Bug 981315: Add ShmemSection and use it for gfxShmSharedReacLock. r=gal
gfx/layers/client/TiledContentClient.cpp
gfx/layers/client/TiledContentClient.h
gfx/layers/composite/TiledContentHost.cpp
gfx/layers/ipc/ISurfaceAllocator.cpp
gfx/layers/ipc/ISurfaceAllocator.h
gfx/layers/ipc/LayersMessages.ipdlh
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -310,19 +310,17 @@ gfxMemorySharedReadLock::GetReadCount()
 
 gfxShmSharedReadLock::gfxShmSharedReadLock(ISurfaceAllocator* aAllocator)
   : mAllocator(aAllocator)
   , mAllocSuccess(false)
 {
   MOZ_COUNT_CTOR(gfxShmSharedReadLock);
   MOZ_ASSERT(mAllocator);
   if (mAllocator) {
-#define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3)
-    if (mAllocator->AllocUnsafeShmem(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)),
-                                     mozilla::ipc::SharedMemory::TYPE_BASIC, &mShmem)) {
+    if (mAllocator->AllocShmemSection(MOZ_ALIGN_WORD(sizeof(ShmReadLockInfo)), &mShmemSection)) {
       ShmReadLockInfo* info = GetShmReadLockInfoPtr();
       info->readCount = 1;
       mAllocSuccess = true;
     }
   }
 }
 
 gfxShmSharedReadLock::~gfxShmSharedReadLock()
@@ -344,17 +342,17 @@ int32_t
 gfxShmSharedReadLock::ReadUnlock() {
   if (!mAllocSuccess) {
     return 0;
   }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
   NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
   if (readCount <= 0) {
-    mAllocator->DeallocShmem(mShmem);
+    mAllocator->FreeShmemSection(mShmemSection);
   }
   return readCount;
 }
 
 int32_t
 gfxShmSharedReadLock::GetReadCount() {
   NS_ASSERT_OWNINGTHREAD(gfxShmSharedReadLock);
   if (!mAllocSuccess) {
@@ -543,20 +541,25 @@ TileClient::GetTileDescriptor()
   }
   MOZ_ASSERT(mFrontLock);
   if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
     // AddRef here and Release when receiving on the host side to make sure the
     // reference count doesn't go to zero before the host receives the message.
     // see TiledLayerBufferComposite::TiledLayerBufferComposite
     mFrontLock->AddRef();
   }
-  return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
-            mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY
-              ? TileLock(uintptr_t(mFrontLock.get()))
-              : TileLock(static_cast<gfxShmSharedReadLock*>(mFrontLock.get())->GetShmem()));
+
+  if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
+    return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+                                  TileLock(uintptr_t(mFrontLock.get())));
+  } else {
+    gfxShmSharedReadLock *lock = static_cast<gfxShmSharedReadLock*>(mFrontLock.get());
+    return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
+                                  TileLock(lock->GetShmemSection()));
+  }
 }
 
 void
 ClientTiledLayerBuffer::ReadUnlock() {
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     if (mRetainedTiles[i].IsPlaceholderTile()) continue;
     mRetainedTiles[i].ReadUnlock();
   }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -103,42 +103,42 @@ public:
   virtual int32_t ReadUnlock() MOZ_OVERRIDE;
 
   virtual int32_t GetReadCount() MOZ_OVERRIDE;
 
   virtual bool IsValid() const MOZ_OVERRIDE { return mAllocSuccess; };
 
   virtual gfxSharedReadLockType GetType() MOZ_OVERRIDE { return TYPE_SHMEM; }
 
-  mozilla::ipc::Shmem& GetShmem() { return mShmem; }
+  mozilla::layers::ShmemSection& GetShmemSection() { return mShmemSection; }
 
   static already_AddRefed<gfxShmSharedReadLock>
-  Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem)
+  Open(mozilla::layers::ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection)
   {
-    nsRefPtr<gfxShmSharedReadLock> readLock = new gfxShmSharedReadLock(aAllocator, aShmem);
+    nsRefPtr<gfxShmSharedReadLock> readLock = new gfxShmSharedReadLock(aAllocator, aShmemSection);
     return readLock.forget();
   }
 
 private:
-  gfxShmSharedReadLock(ISurfaceAllocator* aAllocator, const mozilla::ipc::Shmem& aShmem)
+  gfxShmSharedReadLock(ISurfaceAllocator* aAllocator, const mozilla::layers::ShmemSection& aShmemSection)
     : mAllocator(aAllocator)
-    , mShmem(aShmem)
+    , mShmemSection(aShmemSection)
     , mAllocSuccess(true)
   {
     MOZ_COUNT_CTOR(gfxShmSharedReadLock);
   }
 
   ShmReadLockInfo* GetShmReadLockInfoPtr()
   {
     return reinterpret_cast<ShmReadLockInfo*>
-      (mShmem.get<char>() + mShmem.Size<char>() - sizeof(ShmReadLockInfo));
+      (mShmemSection.shmem().get<char>() + mShmemSection.offset());
   }
 
   RefPtr<ISurfaceAllocator> mAllocator;
-  mozilla::ipc::Shmem mShmem;
+  mozilla::layers::ShmemSection mShmemSection;
   bool mAllocSuccess;
 };
 
 /**
  * Represent a single tile in tiled buffer. The buffer keeps tiles,
  * each tile keeps a reference to a texture client and a read-lock. This
  * read-lock is used to help implement a copy-on-write mechanism. The tile
  * should be locked before being sent to the compositor. The compositor should
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -53,18 +53,18 @@ TiledLayerBufferComposite::TiledLayerBuf
   for(size_t i = 0; i < tiles.Length(); i++) {
     RefPtr<TextureHost> texture;
     const TileDescriptor& tileDesc = tiles[i];
     switch (tileDesc.type()) {
       case TileDescriptor::TTexturedTileDescriptor : {
         texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
         const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
         nsRefPtr<gfxSharedReadLock> sharedLock;
-        if (ipcLock.type() == TileLock::TShmem) {
-          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_Shmem());
+        if (ipcLock.type() == TileLock::TShmemSection) {
+          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
         } else {
           sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
           if (sharedLock) {
             // The corresponding AddRef is in TiledClient::GetTileDescriptor
             sharedLock->Release();
           }
         }
 
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -7,16 +7,17 @@
 
 #include "ISurfaceAllocator.h"
 #include <sys/types.h>                  // for int32_t
 #include "gfx2DGlue.h"                  // for IntSize
 #include "gfxASurface.h"                // for gfxASurface, etc
 #include "gfxPlatform.h"                // for gfxPlatform, gfxImageFormat
 #include "gfxSharedImageSurface.h"      // for gfxSharedImageSurface
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
+#include "mozilla/Atomics.h"            // for PrimitiveIntrinsics
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "ShadowLayerUtils.h"
 #include "mozilla/mozalloc.h"           // for operator delete[], etc
 #include "nsAutoPtr.h"                  // for nsRefPtr, getter_AddRefs, etc
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #ifdef DEBUG
@@ -39,16 +40,24 @@ mozilla::ipc::SharedMemory::SharedMemory
 
 bool
 IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
 {
   return aSurface.type() != SurfaceDescriptor::T__None &&
          aSurface.type() != SurfaceDescriptor::Tnull_t;
 }
 
+ISurfaceAllocator::~ISurfaceAllocator()
+{
+  ShrinkShmemSectionHeap();
+
+  // Check if we're not leaking..
+  MOZ_ASSERT(mUsedShmems.empty());
+}
+
 bool
 ISurfaceAllocator::AllocSharedImageSurface(const gfx::IntSize& aSize,
                                gfxContentType aContent,
                                gfxSharedImageSurface** aBuffer)
 {
   mozilla::ipc::SharedMemory::SharedMemoryType shmemType = OptimalShmemType();
   gfxImageFormat format = gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
 
@@ -172,10 +181,144 @@ ISurfaceAllocator::PlatformAllocSurfaceD
                                                   gfxContentType,
                                                   uint32_t,
                                                   SurfaceDescriptor*)
 {
   return false;
 }
 #endif
 
+// XXX - We should actually figure out the minimum shmem allocation size on
+// a certain platform and use that.
+const uint32_t sShmemPageSize = 4096;
+const uint32_t sSupportedBlockSize = 4;
+
+enum AllocationStatus
+{
+  STATUS_ALLOCATED,
+  STATUS_FREED
+};
+
+struct ShmemSectionHeapHeader
+{
+  Atomic<uint32_t> mTotalBlocks;
+  Atomic<uint32_t> mAllocatedBlocks;
+};
+
+struct ShmemSectionHeapAllocation
+{
+  Atomic<uint32_t> mStatus;
+  uint32_t mSize;
+};
+
+bool
+ISurfaceAllocator::AllocShmemSection(size_t aSize, mozilla::layers::ShmemSection* aShmemSection)
+{
+  // For now we only support sizes of 4. If we want to support different sizes
+  // some more complicated bookkeeping should be added.
+  MOZ_ASSERT(aSize == sSupportedBlockSize);
+  MOZ_ASSERT(aShmemSection);
+
+  uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation));
+
+  for (size_t i = 0; i < mUsedShmems.size(); i++) {
+    ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+    if ((header->mAllocatedBlocks + 1) * allocationSize + sizeof(ShmemSectionHeapHeader) < sShmemPageSize) {
+      aShmemSection->shmem() = mUsedShmems[i];
+      MOZ_ASSERT(mUsedShmems[i].IsWritable());
+      break;
+    }
+  }
+
+  if (!aShmemSection->shmem().IsWritable()) {
+    ipc::Shmem tmp;
+    if (!AllocUnsafeShmem(sShmemPageSize, ipc::SharedMemory::TYPE_BASIC, &tmp)) {
+      return false;
+    }
+
+    ShmemSectionHeapHeader* header = tmp.get<ShmemSectionHeapHeader>();
+    header->mTotalBlocks = 0;
+    header->mAllocatedBlocks = 0;
+
+    mUsedShmems.push_back(tmp);
+    aShmemSection->shmem() = tmp;
+  }
+
+  MOZ_ASSERT(aShmemSection->shmem().IsWritable());
+
+  ShmemSectionHeapHeader* header = aShmemSection->shmem().get<ShmemSectionHeapHeader>();
+  uint8_t* heap = aShmemSection->shmem().get<uint8_t>() + sizeof(ShmemSectionHeapHeader);
+
+  ShmemSectionHeapAllocation* allocHeader = nullptr;
+
+  if (header->mTotalBlocks > header->mAllocatedBlocks) {
+    // Search for the first available block.
+    for (size_t i = 0; i < header->mTotalBlocks; i++) {
+      allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+
+      if (allocHeader->mStatus == STATUS_FREED) {
+        break;
+      }
+      heap += allocationSize;
+    }
+    MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED);
+    MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize);
+  } else {
+    heap += header->mTotalBlocks * allocationSize;
+
+    header->mTotalBlocks++;
+    allocHeader = reinterpret_cast<ShmemSectionHeapAllocation*>(heap);
+    allocHeader->mSize = aSize;
+  }
+
+  MOZ_ASSERT(allocHeader);
+  header->mAllocatedBlocks++;
+  allocHeader->mStatus = STATUS_ALLOCATED;
+
+  aShmemSection->size() = aSize;
+  aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - aShmemSection->shmem().get<uint8_t>();
+  ShrinkShmemSectionHeap();
+  return true;
+}
+
+void
+ISurfaceAllocator::FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection)
+{
+  MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize);
+  MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize);
+
+  ShmemSectionHeapAllocation* allocHeader =
+    reinterpret_cast<ShmemSectionHeapAllocation*>(aShmemSection.shmem().get<char>() +
+                                                  aShmemSection.offset() -
+                                                  sizeof(ShmemSectionHeapAllocation));
+
+  MOZ_ASSERT(allocHeader->mSize == aShmemSection.size());
+
+  DebugOnly<bool> success = allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED);
+  // If this fails something really weird is going on.
+  MOZ_ASSERT(success);
+
+  ShmemSectionHeapHeader* header = aShmemSection.shmem().get<ShmemSectionHeapHeader>();
+  header->mAllocatedBlocks--;
+
+  ShrinkShmemSectionHeap();
+}
+
+void
+ISurfaceAllocator::ShrinkShmemSectionHeap()
+{
+  for (size_t i = 0; i < mUsedShmems.size(); i++) {
+    ShmemSectionHeapHeader* header = mUsedShmems[i].get<ShmemSectionHeapHeader>();
+    if (header->mAllocatedBlocks == 0) {
+      DeallocShmem(mUsedShmems[i]);
+
+      // We don't particularly care about order, move the last one in the array
+      // to this position.
+      mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1];
+      mUsedShmems.pop_back();
+      i--;
+      break;
+    }
+  }
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -9,17 +9,19 @@
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t
 #include "gfxTypes.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/RefPtr.h"
 #include "nsIMemoryReporter.h"          // for nsIMemoryReporter
 #include "mozilla/Atomics.h"            // for Atomic
+#include "mozilla/layers/LayersMessages.h" // for ShmemSection
 #include "LayersTypes.h"
+#include <vector>
 
 /*
  * FIXME [bjacob] *** PURE CRAZYNESS WARNING ***
  *
  * This #define is actually needed here, because subclasses of ISurfaceAllocator,
  * namely ShadowLayerForwarder, will or will not override AllocGrallocBuffer
  * depending on whether MOZ_HAVE_SURFACEDESCRIPTORGRALLOC is defined.
  */
@@ -101,16 +103,30 @@ public:
 
   /**
    * Allocate shared memory that can be accessed by both processes at the
    * same time. Safety is left for the user of the memory to care about.
    */
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 mozilla::ipc::SharedMemory::SharedMemoryType aType,
                                 mozilla::ipc::Shmem* aShmem) = 0;
+
+  /**
+   * Allocate memory in shared memory that can always be accessed by both
+   * processes at a time. Safety is left for the user of the memory to care
+   * about.
+   */
+  bool AllocShmemSection(size_t aSize,
+                         mozilla::layers::ShmemSection* aShmemSection);
+
+  /**
+   * Deallocates a shmem section.
+   */
+  void FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection);
+
   /**
    * Deallocate memory allocated by either AllocShmem or AllocUnsafeShmem.
    */
   virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) = 0;
 
   // was AllocBuffer
   virtual bool AllocSharedImageSurface(const gfx::IntSize& aSize,
                                        gfxContentType aContent,
@@ -153,17 +169,22 @@ protected:
   virtual bool IsOnCompositorSide() const = 0;
   static bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
   virtual bool PlatformAllocSurfaceDescriptor(const gfx::IntSize& aSize,
                                               gfxContentType aContent,
                                               uint32_t aCaps,
                                               SurfaceDescriptor* aBuffer);
 
 
-  virtual ~ISurfaceAllocator() {}
+  virtual ~ISurfaceAllocator();
+
+  void ShrinkShmemSectionHeap();
+
+  // This is used to implement an extremely simple & naive heap allocator.
+  std::vector<mozilla::ipc::Shmem> mUsedShmems;
 
   friend class detail::RefCounted<ISurfaceAllocator, detail::AtomicRefCount>;
 };
 
 class GfxMemoryImageReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -250,18 +250,24 @@ struct OpSetRoot          { PLayer root;
 struct OpInsertAfter      { PLayer container; PLayer childLayer; PLayer after; };
 struct OpPrependChild     { PLayer container; PLayer childLayer; };
 struct OpRemoveChild      { PLayer container; PLayer childLayer; };
 struct OpRepositionChild  { PLayer container; PLayer childLayer; PLayer after; };
 struct OpRaiseToTopChild  { PLayer container; PLayer childLayer; };
 
 struct OpSetDiagnosticTypes { DiagnosticTypes diagnostics; };
 
+struct ShmemSection {
+  Shmem shmem;
+  uint32_t offset;
+  size_t size;
+};
+
 union TileLock {
-  Shmem;
+  ShmemSection;
   uintptr_t;
 };
 
 struct TexturedTileDescriptor {
   PTexture texture;
   TileLock sharedLock;
 };