gecko-dev/gfx/layers/composite/ImageComposite.cpp

211 строки
7.3 KiB
C++

/* -*- 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;
PROFILER_ADD_MARKER("Video frames dropped", GRAPHICS);
}
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;
PROFILER_ADD_MARKER("Video frames dropped", GRAPHICS);
}
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