Bug 1143575. Let ImageContainer::SetCurrentImages accept multiple images. r=nical
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 25 May 2015 14:33:35 +1200
changeset 251713 ced4d3f1a1890fa7acdfa0c614392a9251544198
parent 251712 d5aab92845f150ccecf82ec5da2dc69c477220bb
child 251714 aa8932f9361556bfea1445d00fb6418f84b0fa7c
push id29007
push userryanvm@gmail.com
push dateTue, 07 Jul 2015 18:38:06 +0000
treeherdermozilla-central@9340658848d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1143575
milestone42.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 1143575. Let ImageContainer::SetCurrentImages accept multiple images. r=nical
dom/media/VideoFrameContainer.cpp
gfx/layers/ImageContainer.cpp
gfx/layers/ImageContainer.h
--- a/dom/media/VideoFrameContainer.cpp
+++ b/dom/media/VideoFrameContainer.cpp
@@ -5,17 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VideoFrameContainer.h"
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "nsIFrame.h"
 #include "nsDisplayList.h"
 #include "nsSVGEffects.h"
-#include "ImageContainer.h"
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 
 VideoFrameContainer::VideoFrameContainer(dom::HTMLMediaElement* aElement,
                                          already_AddRefed<ImageContainer> aContainer)
   : mElement(aElement),
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -166,21 +166,20 @@ public:
   ImageContainer* mImageContainer;
 };
 
 ImageContainer::ImageContainer(Mode flag)
 : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mDroppedImageCount(0),
-  mCurrentImageFrameID(0),
-  mCurrentImageComposited(false),
   mImageFactory(new ImageFactory()),
   mRecycleBin(new BufferRecycleBin()),
   mImageClient(nullptr),
+  mCurrentProducerID(-1),
   mIPDLChild(nullptr)
 {
   if (ImageBridgeChild::IsCreated()) {
     // the refcount of this ImageClient is 1. we don't use a RefPtr here because the refcount
     // of this class must be done on the ImageBridge thread.
     switch (flag) {
       case SYNCHRONOUS:
         break;
@@ -231,76 +230,118 @@ ImageContainer::CreateImage(ImageFormat 
     if (img) {
       return img.forget();
     }
   }
   return mImageFactory->CreateImage(aFormat, mScaleHint, mRecycleBin);
 }
 
 void
-ImageContainer::SetCurrentImageInternal(Image *aImage,
-                                        const TimeStamp& aTimeStamp,
-                                        FrameID aFrameID)
+ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-  if (mActiveImage != aImage || aFrameID != mCurrentImageFrameID) {
-    if (!mCurrentImageComposited && !mCurrentImageTimeStamp.IsNull() &&
-        (aTimeStamp.IsNull() || aTimeStamp > mCurrentImageTimeStamp)) {
-      mFrameIDsNotYetComposited.AppendElement(mCurrentImageFrameID);
+  mGenerationCounter = ++sGenerationCounter;
+
+  if (!aImages.IsEmpty()) {
+    NS_ASSERTION(mCurrentImages.IsEmpty() ||
+                 mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
+                 mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
+                 "frame IDs shouldn't go backwards");
+    if (aImages[0].mProducerID != mCurrentProducerID) {
+      mFrameIDsNotYetComposited.Clear();
+      mCurrentProducerID = aImages[0].mProducerID;
+    } else if (!aImages[0].mTimeStamp.IsNull()) {
+      // Check for expired frames
+      for (auto& img : mCurrentImages) {
+        if (img.mProducerID != aImages[0].mProducerID ||
+            img.mTimeStamp.IsNull() ||
+            img.mTimeStamp >= aImages[0].mTimeStamp) {
+          break;
+        }
+        if (!img.mComposited && !img.mTimeStamp.IsNull() &&
+            img.mFrameID != aImages[0].mFrameID) {
+          mFrameIDsNotYetComposited.AppendElement(img.mFrameID);
+        }
+      }
     }
-    mGenerationCounter = ++sGenerationCounter;
-    mCurrentImageFrameID = aFrameID;
-    mCurrentImageComposited = false;
-    mActiveImage = aImage;
-    mCurrentImageTimeStamp = aTimeStamp;
   }
+
+  nsTArray<OwningImage> newImages;
+
+  for (uint32_t i = 0; i < aImages.Length(); ++i) {
+    NS_ASSERTION(aImages[i].mImage, "image can't be null");
+    NS_ASSERTION(!aImages[i].mTimeStamp.IsNull() || aImages.Length() == 1,
+                 "Multiple images require timestamps");
+    if (i > 0) {
+      NS_ASSERTION(aImages[i].mTimeStamp >= aImages[i - 1].mTimeStamp,
+                   "Timestamps must not decrease");
+      NS_ASSERTION(aImages[i].mFrameID > aImages[i - 1].mFrameID,
+                   "FrameIDs must increase");
+      NS_ASSERTION(aImages[i].mProducerID == aImages[i - 1].mProducerID,
+                   "ProducerIDs must be the same");
+    }
+    OwningImage* img = newImages.AppendElement();
+    img->mImage = aImages[i].mImage;
+    img->mTimeStamp = aImages[i].mTimeStamp;
+    img->mFrameID = aImages[i].mFrameID;
+    img->mProducerID = aImages[i].mProducerID;
+    for (auto& oldImg : mCurrentImages) {
+      if (oldImg.mFrameID == img->mFrameID &&
+          oldImg.mProducerID == img->mProducerID) {
+        img->mComposited = oldImg.mComposited;
+        break;
+      }
+    }
+  }
+
+  mCurrentImages.SwapElements(newImages);
 }
 
 void
 ImageContainer::ClearImagesFromImageBridge()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  SetCurrentImageInternal(nullptr, TimeStamp(), 0);
+  SetCurrentImageInternal(nsTArray<NonOwningImage>());
 }
 
 void
 ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
 {
   MOZ_ASSERT(!aImages.IsEmpty());
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   if (IsAsync()) {
     ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
   }
-  MOZ_ASSERT(aImages.Length() == 1);
-  SetCurrentImageInternal(aImages[0].mImage, aImages[0].mTimeStamp,
-                          aImages[0].mFrameID);
+  SetCurrentImageInternal(aImages);
 }
 
  void
 ImageContainer::ClearAllImages()
 {
   if (IsAsync()) {
     // Let ImageClient release all TextureClients. This doesn't return
     // until ImageBridge has called ClearCurrentImageFromImageBridge.
     ImageBridgeChild::FlushAllImages(mImageClient, this);
     return;
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
-  SetCurrentImageInternal(nullptr, TimeStamp(), 0);
+  SetCurrentImageInternal(nsTArray<NonOwningImage>());
 }
 
 void
 ImageContainer::SetCurrentImageInTransaction(Image *aImage)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ASSERTION(!mImageClient, "Should use async image transfer with ImageBridge.");
 
-  SetCurrentImageInternal(aImage, TimeStamp(), 0);
+  nsAutoTArray<NonOwningImage,1> images;
+  images.AppendElement(NonOwningImage(aImage));
+  SetCurrentImageInternal(images);
 }
 
 bool ImageContainer::IsAsync() const
 {
   return mImageClient != nullptr;
 }
 
 uint64_t ImageContainer::GetAsyncContainerID() const
@@ -313,70 +354,69 @@ uint64_t ImageContainer::GetAsyncContain
   }
 }
 
 bool
 ImageContainer::HasCurrentImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-  return !!mActiveImage.get();
+  return !mCurrentImages.IsEmpty();
 }
 
 void
 ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
                                  uint32_t* aGenerationCounter)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-  if (mActiveImage) {
-    OwningImage* img = aImages->AppendElement();
-    img->mImage = mActiveImage;
-    img->mFrameID = mCurrentImageFrameID;
-    img->mProducerID = 0;
-  }
+  *aImages = mCurrentImages;
   if (aGenerationCounter) {
     *aGenerationCounter = mGenerationCounter;
   }
 }
 
 gfx::IntSize
 ImageContainer::GetCurrentSize()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
-  if (!mActiveImage) {
+  if (mCurrentImages.IsEmpty()) {
     return gfx::IntSize(0, 0);
   }
 
-  return mActiveImage->GetSize();
+  return mCurrentImages[0].mImage->GetSize();
 }
 
 void
 ImageContainer::NotifyCompositeInternal(const ImageCompositeNotification& aNotification)
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
 
   // An image composition notification is sent the first time a particular
   // image is composited by an ImageHost. Thus, every time we receive such
   // a notification, a new image has been painted.
   ++mPaintCount;
 
-  while (!mFrameIDsNotYetComposited.IsEmpty()) {
-    if (mFrameIDsNotYetComposited[0] <= aNotification.frameID()) {
-      if (mFrameIDsNotYetComposited[0] < aNotification.frameID()) {
-        ++mDroppedImageCount;
+  if (aNotification.producerID() == mCurrentProducerID) {
+    while (!mFrameIDsNotYetComposited.IsEmpty()) {
+      if (mFrameIDsNotYetComposited[0] <= aNotification.frameID()) {
+        if (mFrameIDsNotYetComposited[0] < aNotification.frameID()) {
+          ++mDroppedImageCount;
+        }
+        mFrameIDsNotYetComposited.RemoveElementAt(0);
+      } else {
+        break;
       }
-      mFrameIDsNotYetComposited.RemoveElementAt(0);
-    } else {
-      break;
     }
-  }
-  if (aNotification.frameID() == mCurrentImageFrameID) {
-    mCurrentImageComposited = true;
+    for (auto& img : mCurrentImages) {
+      if (img.mFrameID == aNotification.frameID()) {
+        img.mComposited = true;
+      }
+    }
   }
 
   if (!aNotification.imageTimeStamp().IsNull()) {
     mPaintDelay = aNotification.firstCompositeTimeStamp() -
         aNotification.imageTimeStamp();
   }
 }
 
@@ -632,10 +672,19 @@ ImageContainer::NotifyComposite(const Im
   if (child) {
     MutexAutoLock lock(child->mLock);
     if (child->mImageContainer) {
       child->mImageContainer->NotifyCompositeInternal(aNotification);
     }
   }
 }
 
+static ImageContainer::ProducerID sProducerID = 0;
+
+ImageContainer::ProducerID
+ImageContainer::AllocateProducerID()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  return ++sProducerID;
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -300,34 +300,40 @@ public:
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
   B2G_ACL_EXPORT already_AddRefed<Image> CreateImage(ImageFormat aFormat);
 
   struct NonOwningImage {
     explicit NonOwningImage(Image* aImage = nullptr,
                             TimeStamp aTimeStamp = TimeStamp(),
-                            FrameID aFrameID = 0)
-      : mImage(aImage), mTimeStamp(aTimeStamp), mFrameID(aFrameID) {}
+                            FrameID aFrameID = 0,
+                            ProducerID aProducerID = 0)
+      : mImage(aImage), mTimeStamp(aTimeStamp), mFrameID(aFrameID),
+        mProducerID(aProducerID) {}
     Image* mImage;
     TimeStamp mTimeStamp;
     FrameID mFrameID;
+    ProducerID mProducerID;
   };
   /**
    * Set aImages as the list of timestamped to display. The Images must have
    * been created by this ImageContainer.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    * aImages must be non-empty. The first timestamp in the list may be
    * null but the others must not be, and the timestamps must increase.
    * Every element of aImages must have non-null mImage.
    * mFrameID can be zero, in which case you won't get meaningful
    * painted/dropped frame counts. Otherwise you should use a unique and
    * increasing ID for each decoded and submitted frame (but it's OK to
    * pass the same frame to SetCurrentImages).
+   * mProducerID is a unique ID for the stream of images. A change in the
+   * mProducerID means changing to a new mFrameID namespace. All frames in
+   * aImages must have the same mProducerID.
    * 
    * The Image data must not be modified after this method is called!
    * Note that this must not be called if ENABLE_ASYNC has not been set.
    *
    * The implementation calls CurrentImageChanged() while holding
    * mReentrantMonitor.
    *
    * If this ImageContainer has an ImageClient for async video:
@@ -385,20 +391,22 @@ public:
   /**
    * Returns if the container currently has an image.
    * Can be called on any thread. This method takes mReentrantMonitor
    * when accessing thread-shared state.
    */
   bool HasCurrentImage();
 
   struct OwningImage {
+    OwningImage() : mFrameID(0), mProducerID(0), mComposited(false) {}
     nsRefPtr<Image> mImage;
     TimeStamp mTimeStamp;
     FrameID mFrameID;
     ProducerID mProducerID;
+    bool mComposited;
   };
   /**
    * Copy the current Image list to aImages.
    * This has to add references since otherwise there are race conditions
    * where the current image is destroyed before the caller can add
    * a reference.
    * Can be called on any thread.
    * May return an empty list to indicate there is no current image.
@@ -455,72 +463,73 @@ public:
    * and painted at least once.  Can be called from any thread.
    */
   uint32_t GetPaintCount() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return mPaintCount;
   }
 
   /**
-   * An image in the current image list "expires" when the image has an
-   * associated timestamp, and in a SetCurrentImages call the timestamp of the
-   * first new image is non-null and greater than the timestamp associated
-   * with the image. Every expired image that is never composited is counted
-   * as dropped.
+   * An entry in the current image list "expires" when the entry has an
+   * non-null timestamp, and in a SetCurrentImages call the new image list is
+   * non-empty, the timestamp of the first new image is non-null and greater
+   * than the timestamp associated with the image, and the first new image's
+   * frameID is not the same as the entry's.
+   * Every expired image that is never composited is counted as dropped.
    */
   uint32_t GetDroppedImageCount()
   {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     return mDroppedImageCount;
   }
 
   PImageContainerChild* GetPImageContainerChild();
   static void NotifyComposite(const ImageCompositeNotification& aNotification);
 
+  /**
+   * Main thread only.
+   */
+  static ProducerID AllocateProducerID();
+
 private:
   typedef mozilla::ReentrantMonitor ReentrantMonitor;
 
   // Private destructor, to discourage deletion outside of Release():
   B2G_ACL_EXPORT ~ImageContainer();
 
-  void SetCurrentImageInternal(Image* aImage, const TimeStamp& aTimeStamp,
-                               FrameID aFrameID);
+  void SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages);
 
   // This is called to ensure we have an active image, this may not be true
   // when we're storing image information in a RemoteImageData structure.
   // NOTE: If we have remote data mRemoteDataMutex should be locked when
   // calling this function!
   void EnsureActiveImage();
 
   void NotifyCompositeInternal(const ImageCompositeNotification& aNotification);
 
   // ReentrantMonitor to protect thread safe access to the "current
   // image", and any other state which is shared between threads.
   ReentrantMonitor mReentrantMonitor;
 
-  nsRefPtr<Image> mActiveImage;
-  TimeStamp mCurrentImageTimeStamp;
+  nsTArray<OwningImage> mCurrentImages;
 
   // Updates every time mActiveImage changes
   uint32_t mGenerationCounter;
 
   // Number of contained images that have been painted at least once.  It's up
   // to the ImageContainer implementation to ensure accesses to this are
   // threadsafe.
   uint32_t mPaintCount;
 
   // See GetPaintDelay. Accessed only with mReentrantMonitor held.
   TimeDuration mPaintDelay;
 
   // See GetDroppedImageCount. Accessed only with mReentrantMonitor held.
   uint32_t mDroppedImageCount;
 
-  FrameID mCurrentImageFrameID;
-  bool mCurrentImageComposited;
-
   // This is the image factory used by this container, layer managers using
   // this container can set an alternative image factory that will be used to
   // create images for this container.
   nsRefPtr<ImageFactory> mImageFactory;
 
   gfx::IntSize mScaleHint;
 
   nsRefPtr<BufferRecycleBin> mRecycleBin;
@@ -530,16 +539,19 @@ private:
   // 'unsuccessful' in this case only means that the ImageClient could not
   // be created, most likely because off-main-thread compositing is not enabled.
   // In this case the ImageContainer is perfectly usable, but it will forward
   // frames to the compositor through transactions in the main thread rather than
   // asynchronusly using the ImageBridge IPDL protocol.
   ImageClient* mImageClient;
 
   nsTArray<FrameID> mFrameIDsNotYetComposited;
+  // ProducerID for last current image(s), including the frames in
+  // mFrameIDsNotYetComposited
+  ProducerID mCurrentProducerID;
 
   // Object must be released on the ImageBridge thread. Field is immutable
   // after creation of the ImageContainer.
   ImageContainerChild* mIPDLChild;
 
   static mozilla::Atomic<uint32_t> sGenerationCounter;
 };