/* -*- 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 #include "mozilla/ScopeExit.h" #include "mozilla/layers/AsyncImagePipelineManager.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/WebRenderTextureHost.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(), mCurrentAsyncImageManager(nullptr) {} WebRenderImageHost::~WebRenderImageHost() { MOZ_ASSERT(mWrBridges.empty()); } void WebRenderImageHost::UseTextureHost( const nsTArray& aTextures) { CompositableHost::UseTextureHost(aTextures); MOZ_ASSERT(aTextures.Length() >= 1); nsTArray 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 (GetAsyncRef()) { for (const auto& it : mWrBridges) { RefPtr wrBridge = it.second->WrBridge(); if (wrBridge && wrBridge->CompositorScheduler()) { wrBridge->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 (mLastFrameID >= 0 && !mWrBridges.empty()) { for (const auto& img : Images()) { bool frameComesAfter = img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID; if (frameComesAfter && !img.mTimeStamp.IsNull()) { for (const auto& it : mWrBridges) { RefPtr wrBridge = it.second->WrBridge(); if (wrBridge) { wrBridge->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; MOZ_ASSERT(mCurrentAsyncImageManager); if (mCurrentAsyncImageManager) { time = mCurrentAsyncImageManager->GetCompositionTime(); } return time; } CompositionOpportunityId WebRenderImageHost::GetCompositionOpportunityId() const { CompositionOpportunityId id; MOZ_ASSERT(mCurrentAsyncImageManager); if (mCurrentAsyncImageManager) { id = mCurrentAsyncImageManager->GetCompositionOpportunityId(); } return id; } void WebRenderImageHost::AppendImageCompositeNotification( const ImageCompositeNotificationInfo& aInfo) const { if (mCurrentAsyncImageManager) { mCurrentAsyncImageManager->AppendImageCompositeNotification(aInfo); } } TextureHost* WebRenderImageHost::GetAsTextureHost(IntRect* aPictureRect) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return nullptr; } TextureHost* WebRenderImageHost::GetAsTextureHostForComposite( AsyncImagePipelineManager* aAsyncImageManager) { mCurrentAsyncImageManager = aAsyncImageManager; const auto onExit = mozilla::MakeScopeExit([&]() { mCurrentAsyncImageManager = nullptr; }); int imageIndex = ChooseImageIndex(); if (imageIndex < 0) { SetCurrentTextureHost(nullptr); return nullptr; } if (uint32_t(imageIndex) + 1 < ImagesCount()) { mCurrentAsyncImageManager->CompositeUntil( GetImage(imageIndex + 1)->mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS)); } const TimedImage* img = GetImage(imageIndex); SetCurrentTextureHost(img->mTextureHost); if (mCurrentAsyncImageManager->GetCompositionTime()) { // We are in a composition. Send ImageCompositeNotifications. OnFinishRendering(imageIndex, img, mAsyncRef.mProcessId, mAsyncRef.mHandle); } 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& 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()); aStream << " [picture-rect=" << img.mPictureRect << "]"; } } void WebRenderImageHost::Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml) { for (const auto& img : Images()) { aStream << aPrefix; aStream << (aDumpHtml ? "
  • TextureHost: " : "TextureHost: "); DumpTextureHost(aStream, img.mTextureHost); aStream << (aDumpHtml ? "
" : " "); } } already_AddRefed WebRenderImageHost::GetAsSurface() { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); 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(const wr::PipelineId& aPipelineId, WebRenderBridgeParent* aWrBridge) { MOZ_ASSERT(aWrBridge); MOZ_ASSERT(!mCurrentAsyncImageManager); #ifdef DEBUG const auto it = mWrBridges.find(wr::AsUint64(aPipelineId)); MOZ_ASSERT(it == mWrBridges.end()); #endif RefPtr ref = aWrBridge->GetWebRenderBridgeParentRef(); mWrBridges.emplace(wr::AsUint64(aPipelineId), ref); } void WebRenderImageHost::ClearWrBridge(const wr::PipelineId& aPipelineId, WebRenderBridgeParent* aWrBridge) { MOZ_ASSERT(aWrBridge); MOZ_ASSERT(!mCurrentAsyncImageManager); const auto it = mWrBridges.find(wr::AsUint64(aPipelineId)); MOZ_ASSERT(it != mWrBridges.end()); if (it == mWrBridges.end()) { gfxCriticalNote << "WrBridge mismatch happened"; return; } mWrBridges.erase(it); SetCurrentTextureHost(nullptr); } } // namespace layers } // namespace mozilla