fune/gfx/layers/wr/WebRenderImageHost.cpp
Jean-Yves Avenard a0a2549c82 Bug 1245400 - P2. Keep track of frames that should have been painted but didn't. r=nical, r=mattwoodrow
We can't rely on the FrameID continuity to determine if a frame has been dropped due to timing or not.
The reason being that the VideoSink will not send to the compositor frames it knows as being late already (causing a discontinuity in the frames IDs), and count them as being dropped.
If we were to look at discontinuity on the compositor we would account for those frames twice.

FramesID will also increase non-linearly if a frame isn't painted because it's not visible (either out of the visible tree or in a hidden tab).

What we can measure however, is when a frame should have been painted but didn't because it was too late by looking at the value returned by ImageComposite::ChooseImageIndex() or when a new set of images is being received by the ImageComposite.
Any images found in the earlier array but never returned must have been dropped due to timing.

Looking at the index continuity greatly simplify the logic as we no longer need to worry if a video is hidden or not, or be part of a layer that is itself hidden as neither SetImages will be called then, nor ChooseImage

For now, we only account for those frames dropped, and do not report them yet.

Differential Revision: https://phabricator.services.mozilla.com/D2176
2018-08-22 17:26:36 +02:00

309 lines
8.7 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 "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
#include "nsPrintfCString.h" // for nsPrintfCString
#include "nsString.h" // for nsAutoCString
namespace mozilla {
using namespace gfx;
namespace layers {
class ISurfaceAllocator;
WebRenderImageHost::WebRenderImageHost(const TextureInfo& aTextureInfo)
: CompositableHost(aTextureInfo)
, ImageComposite()
, mWrBridge(nullptr)
, mWrBridgeBindings(0)
, mUseAsyncImagePipeline(false)
{}
WebRenderImageHost::~WebRenderImageHost()
{
MOZ_ASSERT(!mWrBridge);
}
void
WebRenderImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
{
CompositableHost::UseTextureHost(aTextures);
MOZ_ASSERT(aTextures.Length() >= 1);
nsTArray<TimedImage> newImages;
for (uint32_t i = 0; i < aTextures.Length(); ++i) {
const TimedTexture& t = aTextures[i];
MOZ_ASSERT(t.mTexture);
if (i + 1 < aTextures.Length() &&
t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
// Ignore frames before a frame that we already composited. We don't
// ever want to display these frames. This could be important if
// the frame producer adjusts timestamps (e.g. to track the audio clock)
// and the new frame times are earlier.
continue;
}
TimedImage& img = *newImages.AppendElement();
img.mTextureHost = t.mTexture;
img.mTimeStamp = t.mTimeStamp;
img.mPictureRect = t.mPictureRect;
img.mFrameID = t.mFrameID;
img.mProducerID = t.mProducerID;
img.mTextureHost->SetCropRect(img.mPictureRect);
img.mTextureHost->Updated();
}
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 (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()
{
ClearImages();
SetCurrentTextureHost(nullptr);
}
void
WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture)
{
CompositableHost::RemoveTextureHost(aTexture);
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)
{
const TimedImage* img = ChooseImage();
if (img) {
return img->mTextureHost;
}
return nullptr;
}
TextureHost*
WebRenderImageHost::GetAsTextureHostForComposite()
{
if (!mWrBridge) {
return nullptr;
}
int imageIndex = ChooseImageIndex();
if (imageIndex < 0) {
SetCurrentTextureHost(nullptr);
return nullptr;
}
if (uint32_t(imageIndex) + 1 < ImagesCount()) {
MOZ_ASSERT(mWrBridge->AsyncImageManager());
mWrBridge->AsyncImageManager()->CompositeUntil(
GetImage(imageIndex + 1)->mTimeStamp +
TimeDuration::FromMilliseconds(BIAS_TIME_MS));
}
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);
UpdateBias(imageIndex);
return mCurrentTextureHost;
}
void
WebRenderImageHost::SetCurrentTextureHost(TextureHost* aTexture)
{
if (aTexture == mCurrentTextureHost.get()) {
return;
}
mCurrentTextureHost = aTexture;
}
void WebRenderImageHost::Attach(Layer* aLayer,
TextureSourceProvider* aProvider,
AttachFlags aFlags)
{
}
void
WebRenderImageHost::Composite(Compositor* aCompositor,
LayerComposite* aLayer,
EffectChain& aEffectChain,
float aOpacity,
const gfx::Matrix4x4& aTransform,
const gfx::SamplingFilter aSamplingFilter,
const gfx::IntRect& aClipRect,
const nsIntRegion* aVisibleRegion,
const Maybe<gfx::Polygon>& aGeometry)
{
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
}
void
WebRenderImageHost::SetTextureSourceProvider(TextureSourceProvider* aProvider)
{
if (mTextureSourceProvider != aProvider) {
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 (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)
{
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()
{
const TimedImage* img = ChooseImage();
if (img) {
return img->mTextureHost->GetAsSurface();
}
return nullptr;
}
bool
WebRenderImageHost::Lock()
{
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
return false;
}
void
WebRenderImageHost::Unlock()
{
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
}
IntSize
WebRenderImageHost::GetImageSize()
{
const TimedImage* img = ChooseImage();
if (img) {
return IntSize(img->mPictureRect.Width(), img->mPictureRect.Height());
}
return IntSize();
}
void
WebRenderImageHost::SetWrBridge(WebRenderBridgeParent* aWrBridge)
{
// For image hosts created through ImageBridgeParent, there may be multiple
// references to it due to the order of creation and freeing of layers by
// the layer tree. However this should be limited to things such as video
// which will not be reused across different WebRenderBridgeParent objects.
MOZ_ASSERT(aWrBridge);
MOZ_ASSERT(!mWrBridge || mWrBridge == aWrBridge);
mWrBridge = aWrBridge;
++mWrBridgeBindings;
}
void
WebRenderImageHost::ClearWrBridge()
{
MOZ_ASSERT(mWrBridgeBindings > 0);
--mWrBridgeBindings;
if (mWrBridgeBindings == 0) {
SetCurrentTextureHost(nullptr);
mWrBridge = nullptr;
}
}
} // namespace layers
} // namespace mozilla