DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "ImageComposite.h"

namespace mozilla {

using namespace gfx;

namespace layers {

/* static */ const float ImageComposite::BIAS_TIME_MS = 1.0f;

ImageComposite::ImageComposite()
  : mLastFrameID(-1)
  , mLastProducerID(-1)
  , mBias(BIAS_NONE)
  , mDroppedFrames(0)
  , mLastChosenImageIndex(0)
{
}

ImageComposite::~ImageComposite()
{
}

TimeStamp
ImageComposite::GetBiasedTime(const TimeStamp& aInput) const
{
  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;
  }
}

void
ImageComposite::UpdateBias(size_t aImageIndex)
{
  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 (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.
    mBias = ImageComposite::BIAS_NEGATIVE;
    return;
  }
  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.
    mBias = ImageComposite::BIAS_POSITIVE;
    return;
  }
  mBias = ImageComposite::BIAS_NONE;
}

int
ImageComposite::ChooseImageIndex()
{
  // ChooseImageIndex is called for all images in the layer when it is visible.
  // Change to this behaviour would break dropped frames counting calculation:
  // We rely on this assumption to determine if during successive runs an
  // image is returned that isn't the one following immediately the previous one
  if (mImages.IsEmpty()) {
    return -1;
  }
  TimeStamp now = GetCompositionTime();

  if (now.IsNull()) {
    // Not in a composition, so just return the last image we composited
    // (if it's one of the current images).
    for (uint32_t i = 0; i < mImages.Length(); ++i) {
      if (mImages[i].mFrameID == mLastFrameID &&
          mImages[i].mProducerID == mLastProducerID) {
        return i;
      }
    }
    return -1;
  }

  uint32_t result = mLastChosenImageIndex;
  while (result + 1 < mImages.Length() &&
         GetBiasedTime(mImages[result + 1].mTimeStamp) <= now) {
    ++result;
  }
  if (result - mLastChosenImageIndex > 1) {
    // We're not returning the same image as the last call to ChooseImageIndex
    // or the immediately next one. We can assume that the frames not returned
    // have been dropped as they were too late to be displayed
    mDroppedFrames += result - mLastChosenImageIndex - 1;
  }
  mLastChosenImageIndex = result;
  return result;
}

const ImageComposite::TimedImage* ImageComposite::ChooseImage()
{
  int index = ChooseImageIndex();
  return index >= 0 ? &mImages[index] : nullptr;
}

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()
{
  mImages.Clear();
  mLastChosenImageIndex = 0;
}

uint32_t
ImageComposite::ScanForLastFrameIndex(const nsTArray<TimedImage>& aNewImages)
{
  if (mImages.IsEmpty()) {
    return 0;
  }
  uint32_t i = mLastChosenImageIndex;
  uint32_t newIndex = 0;
  uint32_t dropped = 0;
  // See if the new array of images have any images in common with the
  // previous list that we haven't played yet.
  uint32_t j = 0;
  while (i < mImages.Length() && j < aNewImages.Length()) {
    if (mImages[i].mProducerID != aNewImages[j].mProducerID) {
      // This is new content, can stop.
      newIndex = j;
      break;
    }
    int32_t oldFrameID = mImages[i].mFrameID;
    int32_t newFrameID = aNewImages[j].mFrameID;
    if (oldFrameID > newFrameID) {
      // This is an image we have already returned, we don't need to present
      // it again and can start from this index next time.
      newIndex = ++j;
      continue;
    }
    if (oldFrameID < mLastFrameID) {
      // we have already returned that frame previously, ignore.
      i++;
      continue;
    }
    if (oldFrameID < newFrameID) {
      // This is a new image, all images prior the new one and not yet
      // rendered can be considered as dropped. Those images have a FrameID
      // inferior to the new image.
      for (++i; i < mImages.Length() && mImages[i].mFrameID < newFrameID &&
                mImages[i].mProducerID == aNewImages[j].mProducerID;
           i++) {
        dropped++;
      }
      break;
    }
    i++;
    j++;
  }
  if (dropped > 0) {
    mDroppedFrames += dropped;
  }
  if (newIndex >= aNewImages.Length()) {
    // Somehow none of those images should be rendered (can this happen?)
    // We will always return the last one for now.
    newIndex = aNewImages.Length() - 1;
  }
  return newIndex;
}

void
ImageComposite::SetImages(nsTArray<TimedImage>&& aNewImages)
{
  mLastChosenImageIndex = ScanForLastFrameIndex(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