forked from mirrors/gecko-dev
		
	 a0a2549c82
			
		
	
	
		a0a2549c82
		
	
	
	
	
		
			
			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
		
			
				
	
	
		
			309 lines
		
	
	
	
		
			8.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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
 |