Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/composite/ImageComposite.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ImageComposite.h"
8
9
namespace mozilla {
10
11
using namespace gfx;
12
13
namespace layers {
14
15
/* static */ const float ImageComposite::BIAS_TIME_MS = 1.0f;
16
17
ImageComposite::ImageComposite()
18
  : mLastFrameID(-1)
19
  , mLastProducerID(-1)
20
  , mBias(BIAS_NONE)
21
  , mDroppedFrames(0)
22
  , mLastChosenImageIndex(0)
23
0
{
24
0
}
25
26
ImageComposite::~ImageComposite()
27
0
{
28
0
}
29
30
TimeStamp
31
ImageComposite::GetBiasedTime(const TimeStamp& aInput) const
32
{
33
  switch (mBias) {
34
  case ImageComposite::BIAS_NEGATIVE:
35
    return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
36
  case ImageComposite::BIAS_POSITIVE:
37
    return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
38
  default:
39
    return aInput;
40
  }
41
}
42
43
void
44
ImageComposite::UpdateBias(size_t aImageIndex)
45
0
{
46
0
  MOZ_ASSERT(aImageIndex < ImagesCount());
47
0
48
0
  TimeStamp compositionTime = GetCompositionTime();
49
0
  TimeStamp compositedImageTime = mImages[aImageIndex].mTimeStamp;
50
0
  TimeStamp nextImageTime = aImageIndex + 1 < ImagesCount()
51
0
                              ? mImages[aImageIndex + 1].mTimeStamp
52
0
                              : TimeStamp();
53
0
54
0
  if (compositedImageTime.IsNull()) {
55
0
    mBias = ImageComposite::BIAS_NONE;
56
0
    return;
57
0
  }
58
0
  TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
59
0
  if (compositionTime - compositedImageTime < threshold &&
60
0
      compositionTime - compositedImageTime > -threshold) {
61
0
    // The chosen frame's time is very close to the composition time (probably
62
0
    // just before the current composition time, but due to previously set
63
0
    // negative bias, it could be just after the current composition time too).
64
0
    // If the inter-frame time is almost exactly equal to (a multiple of)
65
0
    // the inter-composition time, then we're in a dangerous situation because
66
0
    // jitter might cause frames to fall one side or the other of the
67
0
    // composition times, causing many frames to be skipped or duplicated.
68
0
    // Try to prevent that by adding a negative bias to the frame times during
69
0
    // the next composite; that should ensure the next frame's time is treated
70
0
    // as falling just before a composite time.
71
0
    mBias = ImageComposite::BIAS_NEGATIVE;
72
0
    return;
73
0
  }
74
0
  if (!nextImageTime.IsNull() &&
75
0
      nextImageTime - compositionTime < threshold &&
76
0
      nextImageTime - compositionTime > -threshold) {
77
0
    // The next frame's time is very close to our composition time (probably
78
0
    // just after the current composition time, but due to previously set
79
0
    // positive bias, it could be just before the current composition time too).
80
0
    // We're in a dangerous situation because jitter might cause frames to
81
0
    // fall one side or the other of the composition times, causing many frames
82
0
    // to be skipped or duplicated.
83
0
    // Try to prevent that by adding a negative bias to the frame times during
84
0
    // the next composite; that should ensure the next frame's time is treated
85
0
    // as falling just before a composite time.
86
0
    mBias = ImageComposite::BIAS_POSITIVE;
87
0
    return;
88
0
  }
89
0
  mBias = ImageComposite::BIAS_NONE;
90
0
}
91
92
int
93
ImageComposite::ChooseImageIndex()
94
0
{
95
0
  // ChooseImageIndex is called for all images in the layer when it is visible.
96
0
  // Change to this behaviour would break dropped frames counting calculation:
97
0
  // We rely on this assumption to determine if during successive runs an
98
0
  // image is returned that isn't the one following immediately the previous one
99
0
  if (mImages.IsEmpty()) {
100
0
    return -1;
101
0
  }
102
0
  TimeStamp now = GetCompositionTime();
103
0
104
0
  if (now.IsNull()) {
105
0
    // Not in a composition, so just return the last image we composited
106
0
    // (if it's one of the current images).
107
0
    for (uint32_t i = 0; i < mImages.Length(); ++i) {
108
0
      if (mImages[i].mFrameID == mLastFrameID &&
109
0
          mImages[i].mProducerID == mLastProducerID) {
110
0
        return i;
111
0
      }
112
0
    }
113
0
    return -1;
114
0
  }
115
0
116
0
  uint32_t result = mLastChosenImageIndex;
117
0
  while (result + 1 < mImages.Length() &&
118
0
         GetBiasedTime(mImages[result + 1].mTimeStamp) <= now) {
119
0
    ++result;
120
0
  }
121
0
  if (result - mLastChosenImageIndex > 1) {
122
0
    // We're not returning the same image as the last call to ChooseImageIndex
123
0
    // or the immediately next one. We can assume that the frames not returned
124
0
    // have been dropped as they were too late to be displayed
125
0
    mDroppedFrames += result - mLastChosenImageIndex - 1;
126
0
  }
127
0
  mLastChosenImageIndex = result;
128
0
  return result;
129
0
}
130
131
const ImageComposite::TimedImage* ImageComposite::ChooseImage()
132
0
{
133
0
  int index = ChooseImageIndex();
134
0
  return index >= 0 ? &mImages[index] : nullptr;
135
0
}
136
137
void
138
ImageComposite::RemoveImagesWithTextureHost(TextureHost* aTexture)
139
0
{
140
0
  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
141
0
    if (mImages[i].mTextureHost == aTexture) {
142
0
      aTexture->UnbindTextureSource();
143
0
      mImages.RemoveElementAt(i);
144
0
    }
145
0
  }
146
0
}
147
148
void
149
ImageComposite::ClearImages()
150
0
{
151
0
  mImages.Clear();
152
0
  mLastChosenImageIndex = 0;
153
0
}
154
155
uint32_t
156
ImageComposite::ScanForLastFrameIndex(const nsTArray<TimedImage>& aNewImages)
157
0
{
158
0
  if (mImages.IsEmpty()) {
159
0
    return 0;
160
0
  }
161
0
  uint32_t i = mLastChosenImageIndex;
162
0
  uint32_t newIndex = 0;
163
0
  uint32_t dropped = 0;
164
0
  // See if the new array of images have any images in common with the
165
0
  // previous list that we haven't played yet.
166
0
  uint32_t j = 0;
167
0
  while (i < mImages.Length() && j < aNewImages.Length()) {
168
0
    if (mImages[i].mProducerID != aNewImages[j].mProducerID) {
169
0
      // This is new content, can stop.
170
0
      newIndex = j;
171
0
      break;
172
0
    }
173
0
    int32_t oldFrameID = mImages[i].mFrameID;
174
0
    int32_t newFrameID = aNewImages[j].mFrameID;
175
0
    if (oldFrameID > newFrameID) {
176
0
      // This is an image we have already returned, we don't need to present
177
0
      // it again and can start from this index next time.
178
0
      newIndex = ++j;
179
0
      continue;
180
0
    }
181
0
    if (oldFrameID < mLastFrameID) {
182
0
      // we have already returned that frame previously, ignore.
183
0
      i++;
184
0
      continue;
185
0
    }
186
0
    if (oldFrameID < newFrameID) {
187
0
      // This is a new image, all images prior the new one and not yet
188
0
      // rendered can be considered as dropped. Those images have a FrameID
189
0
      // inferior to the new image.
190
0
      for (++i; i < mImages.Length() && mImages[i].mFrameID < newFrameID &&
191
0
                mImages[i].mProducerID == aNewImages[j].mProducerID;
192
0
           i++) {
193
0
        dropped++;
194
0
      }
195
0
      break;
196
0
    }
197
0
    i++;
198
0
    j++;
199
0
  }
200
0
  if (dropped > 0) {
201
0
    mDroppedFrames += dropped;
202
0
  }
203
0
  if (newIndex >= aNewImages.Length()) {
204
0
    // Somehow none of those images should be rendered (can this happen?)
205
0
    // We will always return the last one for now.
206
0
    newIndex = aNewImages.Length() - 1;
207
0
  }
208
0
  return newIndex;
209
0
}
210
211
void
212
ImageComposite::SetImages(nsTArray<TimedImage>&& aNewImages)
213
0
{
214
0
  mLastChosenImageIndex = ScanForLastFrameIndex(aNewImages);
215
0
  mImages = std::move(aNewImages);
216
0
}
217
218
const ImageComposite::TimedImage*
219
ImageComposite::GetImage(size_t aIndex) const
220
0
{
221
0
  if (aIndex >= mImages.Length()) {
222
0
    return nullptr;
223
0
  }
224
0
  return &mImages[aIndex];
225
0
}
226
227
} // namespace layers
228
} // namespace mozilla