Bug 1245400 - P1. Make ImageComposite::mImages a private member. r=nical
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 13 Jul 2018 12:42:18 +0200
changeset 490623 257fe22ca35fb323125b6a7058d8bda98d08dfcb
parent 490622 4621d2fd3f175f6d7441874ba2645a47aa26c86c
child 490624 3e0995cbb3dfb2a87dbe00aed20be7c6b7685ccb
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnical
bugs1245400
milestone63.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 1245400 - P1. Make ImageComposite::mImages a private member. r=nical We will use the characteristic of which TimedImage is returned to keep track on how many frames were dropped because they were too old. As such, we must make sure the retrieval of the current image is serialised. This allows to reduce duplicated code between WebRenderImageHost and ImageHost classes. Additionally, make RenderInfo::img member const as really, we never want to modify that one. A future change will enforce that RenderInfo.img never survives longer than the ChooseImage()'s caller to clarify the lifetime of the TimedImage. Differential Revision: https://phabricator.services.mozilla.com/D2175
gfx/layers/composite/ImageComposite.cpp
gfx/layers/composite/ImageComposite.h
gfx/layers/composite/ImageHost.cpp
gfx/layers/composite/ImageHost.h
gfx/layers/wr/WebRenderImageHost.cpp
--- a/gfx/layers/composite/ImageComposite.cpp
+++ b/gfx/layers/composite/ImageComposite.cpp
@@ -19,68 +19,76 @@ ImageComposite::ImageComposite()
   , mLastProducerID(-1)
   , mBias(BIAS_NONE)
 {}
 
 ImageComposite::~ImageComposite()
 {
 }
 
-/* static */ TimeStamp
-ImageComposite::GetBiasedTime(const TimeStamp& aInput, ImageComposite::Bias aBias)
+TimeStamp
+ImageComposite::GetBiasedTime(const TimeStamp& aInput) const
 {
-  switch (aBias) {
+  switch (mBias) {
   case ImageComposite::BIAS_NEGATIVE:
     return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
   case ImageComposite::BIAS_POSITIVE:
     return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
   default:
     return aInput;
   }
 }
 
-/* static */ ImageComposite::Bias
-ImageComposite::UpdateBias(const TimeStamp& aCompositionTime,
-                           const TimeStamp& aCompositedImageTime,
-                           const TimeStamp& aNextImageTime, // may be null
-                           ImageComposite::Bias aBias)
+void
+ImageComposite::UpdateBias(size_t aImageIndex)
 {
-  if (aCompositedImageTime.IsNull()) {
-    return ImageComposite::BIAS_NONE;
+  MOZ_ASSERT(aImageIndex < ImagesCount());
+
+  TimeStamp compositionTime = GetCompositionTime();
+  TimeStamp compositedImageTime = mImages[aImageIndex].mTimeStamp;
+  TimeStamp nextImageTime = aImageIndex + 1 < ImagesCount()
+                              ? mImages[aImageIndex + 1].mTimeStamp
+                              : TimeStamp();
+
+  if (compositedImageTime.IsNull()) {
+    mBias = ImageComposite::BIAS_NONE;
+    return;
   }
   TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
-  if (aCompositionTime - aCompositedImageTime < threshold &&
-      aCompositionTime - aCompositedImageTime > -threshold) {
+  if (compositionTime - compositedImageTime < threshold &&
+      compositionTime - compositedImageTime > -threshold) {
     // The chosen frame's time is very close to the composition time (probably
     // just before the current composition time, but due to previously set
     // negative bias, it could be just after the current composition time too).
     // If the inter-frame time is almost exactly equal to (a multiple of)
     // the inter-composition time, then we're in a dangerous situation because
     // jitter might cause frames to fall one side or the other of the
     // composition times, causing many frames to be skipped or duplicated.
     // Try to prevent that by adding a negative bias to the frame times during
     // the next composite; that should ensure the next frame's time is treated
     // as falling just before a composite time.
-    return ImageComposite::BIAS_NEGATIVE;
+    mBias = ImageComposite::BIAS_NEGATIVE;
+    return;
   }
-  if (!aNextImageTime.IsNull() &&
-      aNextImageTime - aCompositionTime < threshold &&
-      aNextImageTime - aCompositionTime > -threshold) {
+  if (!nextImageTime.IsNull() &&
+      nextImageTime - compositionTime < threshold &&
+      nextImageTime - compositionTime > -threshold) {
     // The next frame's time is very close to our composition time (probably
     // just after the current composition time, but due to previously set
     // positive bias, it could be just before the current composition time too).
     // We're in a dangerous situation because jitter might cause frames to
     // fall one side or the other of the composition times, causing many frames
     // to be skipped or duplicated.
     // Try to prevent that by adding a negative bias to the frame times during
     // the next composite; that should ensure the next frame's time is treated
     // as falling just before a composite time.
-    return ImageComposite::BIAS_POSITIVE;
+    mBias = ImageComposite::BIAS_POSITIVE;
+    return;
   }
-  return ImageComposite::BIAS_NONE;
+  mBias = ImageComposite::BIAS_NONE;
 }
 
 int
 ImageComposite::ChooseImageIndex() const
 {
   if (mImages.IsEmpty()) {
     return -1;
   }
@@ -95,28 +103,54 @@ ImageComposite::ChooseImageIndex() const
         return i;
       }
     }
     return -1;
   }
 
   uint32_t result = 0;
   while (result + 1 < mImages.Length() &&
-      GetBiasedTime(mImages[result + 1].mTimeStamp, mBias) <= now) {
+         GetBiasedTime(mImages[result + 1].mTimeStamp) <= now) {
     ++result;
   }
   return result;
 }
 
 const ImageComposite::TimedImage* ImageComposite::ChooseImage() const
 {
   int index = ChooseImageIndex();
   return index >= 0 ? &mImages[index] : nullptr;
 }
 
-ImageComposite::TimedImage* ImageComposite::ChooseImage()
+void
+ImageComposite::RemoveImagesWithTextureHost(TextureHost* aTexture)
+{
+  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
+    if (mImages[i].mTextureHost == aTexture) {
+      aTexture->UnbindTextureSource();
+      mImages.RemoveElementAt(i);
+    }
+  }
+}
+
+void
+ImageComposite::ClearImages()
 {
-  int index = ChooseImageIndex();
-  return index >= 0 ? &mImages[index] : nullptr;
+  mImages.Clear();
+}
+
+void
+ImageComposite::SetImages(nsTArray<TimedImage>&& aNewImages)
+{
+  mImages = std::move(aNewImages);
+}
+
+const ImageComposite::TimedImage*
+ImageComposite::GetImage(size_t aIndex) const
+{
+  if (aIndex >= mImages.Length()) {
+    return nullptr;
+  }
+  return &mImages[aIndex];
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/ImageComposite.h
+++ b/gfx/layers/composite/ImageComposite.h
@@ -45,23 +45,18 @@ public:
     // Don't apply bias to frame times
     BIAS_NONE,
     // Apply a negative bias to frame times to keep them before the vsync time
     BIAS_NEGATIVE,
     // Apply a positive bias to frame times to keep them after the vsync time
     BIAS_POSITIVE,
   };
 
-  static TimeStamp GetBiasedTime(const TimeStamp& aInput, ImageComposite::Bias aBias);
-
 protected:
-  static Bias UpdateBias(const TimeStamp& aCompositionTime,
-                         const TimeStamp& aCompositedImageTime,
-                         const TimeStamp& aNextImageTime, // may be null
-                         ImageComposite::Bias aBias);
+  void UpdateBias(size_t aImageIndex);
 
   virtual TimeStamp GetCompositionTime() const = 0;
 
   struct TimedImage {
     CompositableTextureHostRef mTextureHost;
     TimeStamp mTimeStamp;
     gfx::IntRect mPictureRect;
     int32_t mFrameID;
@@ -70,22 +65,31 @@ protected:
 
   /**
    * ChooseImage is guaranteed to return the same TimedImage every time it's
    * called during the same composition, up to the end of Composite() ---
    * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
    * mBias is updated at the end of Composite().
    */
   const TimedImage* ChooseImage() const;
-  TimedImage* ChooseImage();
   int ChooseImageIndex() const;
+  const TimedImage* GetImage(size_t aIndex) const;
+  size_t ImagesCount() const { return mImages.Length(); }
+  const nsTArray<TimedImage>& Images() const { return mImages; }
 
-  nsTArray<TimedImage> mImages;
+  void RemoveImagesWithTextureHost(TextureHost* aTexture);
+  void ClearImages();
+  void SetImages(nsTArray<TimedImage>&& aNewImages);
+
   int32_t mLastFrameID;
   int32_t mLastProducerID;
+
+private:
+  nsTArray<TimedImage> mImages;
+  TimeStamp GetBiasedTime(const TimeStamp& aInput) const;
   /**
    * Bias to apply to the next frame.
    */
   Bias mBias;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -4,16 +4,17 @@
  * 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 "ImageHost.h"
 
 #include "LayersLogging.h"              // for AppendToString
 #include "composite/CompositableHost.h"  // for CompositableHost, etc
 #include "ipc/IPCMessageUtils.h"        // for null_t
+#include "mozilla/Move.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
@@ -61,37 +62,36 @@ ImageHost::UseTextureHost(const nsTArray
     img.mTimeStamp = t.mTimeStamp;
     img.mPictureRect = t.mPictureRect;
     img.mFrameID = t.mFrameID;
     img.mProducerID = t.mProducerID;
     img.mTextureHost->SetCropRect(img.mPictureRect);
     img.mTextureHost->Updated();
   }
 
-  mImages.SwapElements(newImages);
-  newImages.Clear();
+  SetImages(std::move(newImages));
 
   // If we only have one image we can upload it right away, otherwise we'll upload
   // on-demand during composition after we have picked the proper timestamp.
-  if (mImages.Length() == 1) {
-    SetCurrentTextureHost(mImages[0].mTextureHost);
+  if (ImagesCount() == 1) {
+    SetCurrentTextureHost(GetImage(0)->mTextureHost);
   }
 
   HostLayerManager* lm = GetLayerManager();
 
   // Video producers generally send replacement images with the same frameID but
   // slightly different timestamps in order to sync with the audio clock. This
   // means that any CompositeUntil() call we made in Composite() may no longer
   // guarantee that we'll composite until the next frame is ready. Fix that here.
   if (lm && mLastFrameID >= 0) {
-    for (size_t i = 0; i < mImages.Length(); ++i) {
-      bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
-                             mImages[i].mProducerID != mLastProducerID;
-      if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
-        lm->CompositeUntil(mImages[i].mTimeStamp +
+    for (const auto& img : Images()) {
+      bool frameComesAfter =
+        img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID;
+      if (frameComesAfter && !img.mTimeStamp.IsNull()) {
+        lm->CompositeUntil(img.mTimeStamp +
                            TimeDuration::FromMilliseconds(BIAS_TIME_MS));
         break;
       }
     }
   }
 }
 
 void
@@ -137,54 +137,49 @@ ImageHost::CleanupResources()
 }
 
 void
 ImageHost::RemoveTextureHost(TextureHost* aTexture)
 {
   MOZ_ASSERT(!mLocked);
 
   CompositableHost::RemoveTextureHost(aTexture);
-
-  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
-    if (mImages[i].mTextureHost == aTexture) {
-      aTexture->UnbindTextureSource();
-      mImages.RemoveElementAt(i);
-    }
-  }
+  RemoveImagesWithTextureHost(aTexture);
 }
 
 TimeStamp
 ImageHost::GetCompositionTime() const
 {
   TimeStamp time;
   if (HostLayerManager* lm = GetLayerManager()) {
     time = lm->GetCompositionTime();
   }
   return time;
 }
 
 TextureHost*
 ImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
-  TimedImage* img = ChooseImage();
-  if (img) {
-    SetCurrentTextureHost(img->mTextureHost);
+  const TimedImage* img = ChooseImage();
+  if (!img) {
+    return nullptr;
   }
-  if (aPictureRect && img) {
+  SetCurrentTextureHost(img->mTextureHost);
+  if (aPictureRect) {
     *aPictureRect = img->mPictureRect;
   }
-  return img ? img->mTextureHost.get() : nullptr;
+  return img->mTextureHost;
 }
 
 void ImageHost::Attach(Layer* aLayer,
                        TextureSourceProvider* aProvider,
                        AttachFlags aFlags)
 {
   CompositableHost::Attach(aLayer, aProvider, aFlags);
-  for (auto& img : mImages) {
+  for (const auto& img : Images()) {
     img.mTextureHost->SetTextureSourceProvider(aProvider);
     img.mTextureHost->Updated();
   }
 }
 
 void
 ImageHost::Composite(Compositor* aCompositor,
                      LayerComposite* aLayer,
@@ -196,17 +191,17 @@ ImageHost::Composite(Compositor* aCompos
                      const nsIntRegion* aVisibleRegion,
                      const Maybe<gfx::Polygon>& aGeometry)
 {
   RenderInfo info;
   if (!PrepareToRender(aCompositor, &info)) {
     return;
   }
 
-  TimedImage* img = info.img;
+  const TimedImage* img = info.img;
 
   {
     AutoLockCompositableHost autoLock(this);
     if (autoLock.Failed()) {
       NS_WARNING("failed to lock front buffer");
       return;
     }
 
@@ -314,21 +309,22 @@ ImageHost::PrepareToRender(TextureSource
     return false;
   }
 
   int imageIndex = ChooseImageIndex();
   if (imageIndex < 0) {
     return false;
   }
 
-  if (uint32_t(imageIndex) + 1 < mImages.Length()) {
-    lm->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+  if (uint32_t(imageIndex) + 1 < ImagesCount()) {
+    lm->CompositeUntil(GetImage(imageIndex + 1)->mTimeStamp +
+                       TimeDuration::FromMilliseconds(BIAS_TIME_MS));
   }
 
-  TimedImage* img = &mImages[imageIndex];
+  const TimedImage* img = GetImage(imageIndex);
   img->mTextureHost->SetTextureSourceProvider(aProvider);
   SetCurrentTextureHost(img->mTextureHost);
 
   aOutInfo->imageIndex = imageIndex;
   aOutInfo->img = img;
   aOutInfo->host = mCurrentTextureHost;
   return true;
 }
@@ -342,17 +338,17 @@ ImageHost::AcquireTextureSource(const Re
   }
   return mCurrentTextureSource.get();
 }
 
 void
 ImageHost::FinishRendering(const RenderInfo& aInfo)
 {
   HostLayerManager* lm = GetLayerManager();
-  TimedImage* img = aInfo.img;
+  const TimedImage* img = aInfo.img;
   int imageIndex = aInfo.imageIndex;
 
   if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
     if (mAsyncRef) {
       ImageCompositeNotificationInfo info;
       info.mImageBridgeProcessId = mAsyncRef.mProcessId;
       info.mNotification = ImageCompositeNotification(
         mAsyncRef.mHandle,
@@ -364,78 +360,74 @@ ImageHost::FinishRendering(const RenderI
     mLastProducerID = img->mProducerID;
   }
 
   // Update mBias last. This can change which frame ChooseImage(Index) would
   // return, and we don't want to do that until we've finished compositing
   // since callers of ChooseImage(Index) assume the same image will be chosen
   // during a given composition. This must happen after autoLock's
   // destructor!
-  mBias = UpdateBias(
-      lm->GetCompositionTime(), mImages[imageIndex].mTimeStamp,
-      uint32_t(imageIndex + 1) < mImages.Length() ?
-          mImages[imageIndex + 1].mTimeStamp : TimeStamp(),
-      mBias);
+    UpdateBias(imageIndex);
 }
 
 void
 ImageHost::SetTextureSourceProvider(TextureSourceProvider* aProvider)
 {
   if (mTextureSourceProvider != aProvider) {
-    for (auto& img : mImages) {
+    for (const auto& img : Images()) {
       img.mTextureHost->SetTextureSourceProvider(aProvider);
     }
   }
   CompositableHost::SetTextureSourceProvider(aProvider);
 }
 
 void
 ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
 
   nsAutoCString pfx(aPrefix);
   pfx += "  ";
-  for (auto& img : mImages) {
+  for (const auto& img : Images()) {
     aStream << "\n";
     img.mTextureHost->PrintInfo(aStream, pfx.get());
     AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
   }
 }
 
 void
 ImageHost::Dump(std::stringstream& aStream,
                 const char* aPrefix,
                 bool aDumpHtml)
 {
-  for (auto& img : mImages) {
+  for (const auto& img : Images()) {
     aStream << aPrefix;
     aStream << (aDumpHtml ? "<ul><li>TextureHost: "
                              : "TextureHost: ");
     DumpTextureHost(aStream, img.mTextureHost);
     aStream << (aDumpHtml ? " </li></ul> " : " ");
   }
 }
 
 already_AddRefed<gfx::DataSourceSurface>
 ImageHost::GetAsSurface()
 {
-  TimedImage* img = ChooseImage();
+  const TimedImage* img = ChooseImage();
   if (img) {
     return img->mTextureHost->GetAsSurface();
   }
   return nullptr;
 }
 
 bool
 ImageHost::Lock()
 {
   MOZ_ASSERT(!mLocked);
-  TimedImage* img = ChooseImage();
+  const TimedImage* img = ChooseImage();
   if (!img) {
     return false;
   }
 
   SetCurrentTextureHost(img->mTextureHost);
 
   if (!mCurrentTextureHost->Lock()) {
     return false;
@@ -484,17 +476,17 @@ ImageHost::IsOpaque()
     return true;
   }
   return false;
 }
 
 already_AddRefed<TexturedEffect>
 ImageHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
 {
-  TimedImage* img = ChooseImage();
+  const TimedImage* img = ChooseImage();
   if (!img) {
     return nullptr;
   }
   SetCurrentTextureHost(img->mTextureHost);
   if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
     return nullptr;
   }
   bool isAlphaPremultiplied = true;
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -88,17 +88,17 @@ public:
   void SetCurrentTextureHost(TextureHost* aTexture);
 
   virtual void CleanupResources() override;
 
   bool IsOpaque();
 
   struct RenderInfo {
     int imageIndex;
-    TimedImage* img;
+    const TimedImage* img;
     RefPtr<TextureHost> host;
 
     RenderInfo() : imageIndex(-1), img(nullptr)
     {}
   };
 
   // Acquire rendering information for the current frame.
   bool PrepareToRender(TextureSourceProvider* aProvider, RenderInfo* aOutInfo);
--- a/gfx/layers/wr/WebRenderImageHost.cpp
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "WebRenderImageHost.h"
 
 #include "LayersLogging.h"
+#include "mozilla/Move.h"
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorVsyncScheduler.h"  // for CompositorVsyncScheduler
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/WebRenderBridgeParent.h"
 #include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
@@ -63,86 +64,77 @@ WebRenderImageHost::UseTextureHost(const
     img.mTimeStamp = t.mTimeStamp;
     img.mPictureRect = t.mPictureRect;
     img.mFrameID = t.mFrameID;
     img.mProducerID = t.mProducerID;
     img.mTextureHost->SetCropRect(img.mPictureRect);
     img.mTextureHost->Updated();
   }
 
-  mImages.SwapElements(newImages);
-  newImages.Clear();
+  SetImages(std::move(newImages));
 
   if (mWrBridge && mWrBridge->CompositorScheduler() && GetAsyncRef()) {
     // Will check if we will generate frame.
     mWrBridge->CompositorScheduler()->ScheduleComposition();
   }
 
   // Video producers generally send replacement images with the same frameID but
   // slightly different timestamps in order to sync with the audio clock. This
   // means that any CompositeUntil() call we made in Composite() may no longer
   // guarantee that we'll composite until the next frame is ready. Fix that here.
   if (mWrBridge && mLastFrameID >= 0) {
     MOZ_ASSERT(mWrBridge->AsyncImageManager());
-    for (size_t i = 0; i < mImages.Length(); ++i) {
-      bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
-                             mImages[i].mProducerID != mLastProducerID;
-      if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
-        mWrBridge->AsyncImageManager()->CompositeUntil(mImages[i].mTimeStamp +
-                           TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+    for (const auto& img : Images()) {
+      bool frameComesAfter =
+        img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID;
+      if (frameComesAfter && !img.mTimeStamp.IsNull()) {
+        mWrBridge->AsyncImageManager()->CompositeUntil(
+          img.mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
         break;
       }
     }
   }
 }
 
 void
 WebRenderImageHost::UseComponentAlphaTextures(TextureHost* aTextureOnBlack,
                                               TextureHost* aTextureOnWhite)
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
 }
 
 void
 WebRenderImageHost::CleanupResources()
 {
-  nsTArray<TimedImage> newImages;
-  mImages.SwapElements(newImages);
-  newImages.Clear();
+  ClearImages();
   SetCurrentTextureHost(nullptr);
 }
 
 void
 WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture)
 {
   CompositableHost::RemoveTextureHost(aTexture);
-
-  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
-    if (mImages[i].mTextureHost == aTexture) {
-      aTexture->UnbindTextureSource();
-      mImages.RemoveElementAt(i);
-    }
-  }
+  RemoveImagesWithTextureHost(aTexture);
 }
 
 TimeStamp
 WebRenderImageHost::GetCompositionTime() const
 {
   TimeStamp time;
   if (mWrBridge) {
     MOZ_ASSERT(mWrBridge->AsyncImageManager());
     time = mWrBridge->AsyncImageManager()->GetCompositionTime();
   }
   return time;
 }
 
 TextureHost*
 WebRenderImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
-  TimedImage* img = ChooseImage();
+  const TimedImage* img = ChooseImage();
   if (img) {
     return img->mTextureHost;
   }
   return nullptr;
 }
 
 TextureHost*
 WebRenderImageHost::GetAsTextureHostForComposite()
@@ -152,44 +144,41 @@ WebRenderImageHost::GetAsTextureHostForC
   }
 
   int imageIndex = ChooseImageIndex();
   if (imageIndex < 0) {
     SetCurrentTextureHost(nullptr);
     return nullptr;
   }
 
-  if (uint32_t(imageIndex) + 1 < mImages.Length()) {
+  if (uint32_t(imageIndex) + 1 < ImagesCount()) {
     MOZ_ASSERT(mWrBridge->AsyncImageManager());
-    mWrBridge->AsyncImageManager()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
+    mWrBridge->AsyncImageManager()->CompositeUntil(
+      GetImage(imageIndex + 1)->mTimeStamp +
+      TimeDuration::FromMilliseconds(BIAS_TIME_MS));
   }
 
-  TimedImage* img = &mImages[imageIndex];
+  const TimedImage* img = GetImage(imageIndex);
 
   if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
     if (mAsyncRef) {
       ImageCompositeNotificationInfo info;
       info.mImageBridgeProcessId = mAsyncRef.mProcessId;
       info.mNotification = ImageCompositeNotification(
         mAsyncRef.mHandle,
         img->mTimeStamp, mWrBridge->AsyncImageManager()->GetCompositionTime(),
         img->mFrameID, img->mProducerID);
       mWrBridge->AsyncImageManager()->AppendImageCompositeNotification(info);
     }
     mLastFrameID = img->mFrameID;
     mLastProducerID = img->mProducerID;
   }
   SetCurrentTextureHost(img->mTextureHost);
 
-  mBias = UpdateBias(
-    mWrBridge->AsyncImageManager()->GetCompositionTime(),
-    mImages[imageIndex].mTimeStamp,
-    uint32_t(imageIndex + 1) < mImages.Length() ?
-      mImages[imageIndex + 1].mTimeStamp : TimeStamp(),
-    mBias);
+  UpdateBias(imageIndex);
 
   return mCurrentTextureHost;
 }
 
 void
 WebRenderImageHost::SetCurrentTextureHost(TextureHost* aTexture)
 {
   if (aTexture == mCurrentTextureHost.get()) {
@@ -218,56 +207,56 @@ WebRenderImageHost::Composite(Compositor
 {
   MOZ_ASSERT_UNREACHABLE("unexpected to be called");
 }
 
 void
 WebRenderImageHost::SetTextureSourceProvider(TextureSourceProvider* aProvider)
 {
   if (mTextureSourceProvider != aProvider) {
-    for (auto& img : mImages) {
+    for (const auto& img : Images()) {
       img.mTextureHost->SetTextureSourceProvider(aProvider);
     }
   }
   CompositableHost::SetTextureSourceProvider(aProvider);
 }
 
 void
 WebRenderImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("WebRenderImageHost (0x%p)", this).get();
 
   nsAutoCString pfx(aPrefix);
   pfx += "  ";
-  for (auto& img : mImages) {
+  for (const auto& img : Images()) {
     aStream << "\n";
     img.mTextureHost->PrintInfo(aStream, pfx.get());
     AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
   }
 }
 
 void
 WebRenderImageHost::Dump(std::stringstream& aStream,
-                const char* aPrefix,
-                bool aDumpHtml)
+                         const char* aPrefix,
+                         bool aDumpHtml)
 {
-  for (auto& img : mImages) {
+  for (const auto& img : Images()) {
     aStream << aPrefix;
     aStream << (aDumpHtml ? "<ul><li>TextureHost: "
                              : "TextureHost: ");
     DumpTextureHost(aStream, img.mTextureHost);
     aStream << (aDumpHtml ? " </li></ul> " : " ");
   }
 }
 
 already_AddRefed<gfx::DataSourceSurface>
 WebRenderImageHost::GetAsSurface()
 {
-  TimedImage* img = ChooseImage();
+  const TimedImage* img = ChooseImage();
   if (img) {
     return img->mTextureHost->GetAsSurface();
   }
   return nullptr;
 }
 
 bool
 WebRenderImageHost::Lock()