/* -*- 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 "mozilla/layers/WebRenderBridgeChild.h" #include "gfxPlatform.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/layers/CompositableClient.h" #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/IpcResourceUpdateQueue.h" #include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/PTextureChild.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/webrender/WebRenderAPI.h" #include "PDMFactory.h" namespace mozilla { namespace layers { using namespace mozilla::gfx; WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId) : mIsInTransaction(false), mIsInClearCachedResources(false), mIdNamespace{0}, mResourceId(0), mPipelineId(aPipelineId), mManager(nullptr), mIPCOpen(false), mDestroyed(false), mSentDisplayList(false), mFontKeysDeleted(0), mFontInstanceKeysDeleted(0) {} WebRenderBridgeChild::~WebRenderBridgeChild() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mDestroyed); } void WebRenderBridgeChild::Destroy(bool aIsSync) { if (!IPCOpen()) { return; } DoDestroy(); if (aIsSync) { SendShutdownSync(); } else { SendShutdown(); } } void WebRenderBridgeChild::ActorDestroy(ActorDestroyReason why) { DoDestroy(); } void WebRenderBridgeChild::DoDestroy() { if (RefCountedShm::IsValid(mResourceShm) && RefCountedShm::Release(mResourceShm) == 0) { RefCountedShm::Dealloc(this, mResourceShm); mResourceShm = RefCountedShmem(); } // mDestroyed is used to prevent calling Send__delete__() twice. // When this function is called from CompositorBridgeChild::Destroy(). // mActiveResourceTracker is not cleared here, since it is // used by PersistentBufferProviderShared. mDestroyed = true; mManager = nullptr; } void WebRenderBridgeChild::AddWebRenderParentCommand( const WebRenderParentCommand& aCmd) { mParentCommands.AppendElement(aCmd); } void WebRenderBridgeChild::BeginTransaction() { MOZ_ASSERT(!mDestroyed); UpdateFwdTransactionId(); mIsInTransaction = true; } void WebRenderBridgeChild::UpdateResources( wr::IpcResourceUpdateQueue& aResources) { if (!IPCOpen()) { aResources.Clear(); return; } if (aResources.IsEmpty()) { return; } nsTArray resourceUpdates; nsTArray smallShmems; nsTArray largeShmems; aResources.Flush(resourceUpdates, smallShmems, largeShmems); this->SendUpdateResources(mIdNamespace, resourceUpdates, smallShmems, std::move(largeShmems)); } bool WebRenderBridgeChild::EndTransaction( DisplayListData&& aDisplayListData, TransactionId aTransactionId, bool aContainsSVGGroup, const mozilla::VsyncId& aVsyncId, const mozilla::TimeStamp& aVsyncStartTime, const mozilla::TimeStamp& aRefreshStartTime, const mozilla::TimeStamp& aTxnStartTime, const nsCString& aTxnURL) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(mIsInTransaction); TimeStamp fwdTime = TimeStamp::Now(); aDisplayListData.mCommands = std::move(mParentCommands); aDisplayListData.mIdNamespace = mIdNamespace; nsTArray payloads; if (mManager) { mManager->TakeCompositionPayloads(payloads); } mSentDisplayList = true; bool ret = this->SendSetDisplayList( std::move(aDisplayListData), mDestroyedActors, GetFwdTransactionId(), aTransactionId, aContainsSVGGroup, aVsyncId, aVsyncStartTime, aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime, payloads); // With multiple render roots, we may not have sent all of our // mParentCommands, so go ahead and go through our mParentCommands and ensure // they get sent. ProcessWebRenderParentCommands(); mDestroyedActors.Clear(); mIsInTransaction = false; return ret; } void WebRenderBridgeChild::EndEmptyTransaction( const FocusTarget& aFocusTarget, Maybe&& aTransactionData, TransactionId aTransactionId, const mozilla::VsyncId& aVsyncId, const mozilla::TimeStamp& aVsyncStartTime, const mozilla::TimeStamp& aRefreshStartTime, const mozilla::TimeStamp& aTxnStartTime, const nsCString& aTxnURL) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(mIsInTransaction); TimeStamp fwdTime = TimeStamp::Now(); if (aTransactionData) { aTransactionData->mCommands = std::move(mParentCommands); } nsTArray payloads; if (mManager) { mManager->TakeCompositionPayloads(payloads); } this->SendEmptyTransaction( aFocusTarget, std::move(aTransactionData), mDestroyedActors, GetFwdTransactionId(), aTransactionId, aVsyncId, aVsyncStartTime, aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime, payloads); // With multiple render roots, we may not have sent all of our // mParentCommands, so go ahead and go through our mParentCommands and ensure // they get sent. ProcessWebRenderParentCommands(); mDestroyedActors.Clear(); mIsInTransaction = false; } void WebRenderBridgeChild::ProcessWebRenderParentCommands() { MOZ_ASSERT(!mDestroyed); if (!mParentCommands.IsEmpty()) { this->SendParentCommands(mParentCommands); mParentCommands.Clear(); } } void WebRenderBridgeChild::AddPipelineIdForAsyncCompositable( const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) { AddWebRenderParentCommand( OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ true)); } void WebRenderBridgeChild::AddPipelineIdForCompositable( const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) { AddWebRenderParentCommand(OpAddPipelineIdForCompositable( aPipelineId, aHandle, /* isAsync */ false)); } void WebRenderBridgeChild::RemovePipelineIdForCompositable( const wr::PipelineId& aPipelineId) { AddWebRenderParentCommand(OpRemovePipelineIdForCompositable(aPipelineId)); } wr::ExternalImageId WebRenderBridgeChild::GetNextExternalImageId() { wr::MaybeExternalImageId id = GetCompositorBridgeChild()->GetNextExternalImageId(); MOZ_RELEASE_ASSERT(id.isSome()); return id.value(); } void WebRenderBridgeChild::ReleaseTextureOfImage(const wr::ImageKey& aKey) { AddWebRenderParentCommand(OpReleaseTextureOfImage(aKey)); } struct FontFileDataSink { wr::FontKey* mFontKey; WebRenderBridgeChild* mWrBridge; wr::IpcResourceUpdateQueue* mResources; }; static void WriteFontFileData(const uint8_t* aData, uint32_t aLength, uint32_t aIndex, void* aBaton) { FontFileDataSink* sink = static_cast(aBaton); *sink->mFontKey = sink->mWrBridge->GetNextFontKey(); sink->mResources->AddRawFont( *sink->mFontKey, Range(const_cast(aData), aLength), aIndex); } static void WriteFontDescriptor(const uint8_t* aData, uint32_t aLength, uint32_t aIndex, void* aBaton) { FontFileDataSink* sink = static_cast(aBaton); *sink->mFontKey = sink->mWrBridge->GetNextFontKey(); sink->mResources->AddFontDescriptor( *sink->mFontKey, Range(const_cast(aData), aLength), aIndex); } void WebRenderBridgeChild::PushGlyphs( wr::DisplayListBuilder& aBuilder, Range aGlyphs, gfx::ScaledFont* aFont, const wr::ColorF& aColor, const StackingContextHelper& aSc, const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aBackfaceVisible, const wr::GlyphOptions* aGlyphOptions) { MOZ_ASSERT(aFont); Maybe key = GetFontKeyForScaledFont(aFont); MOZ_ASSERT(key.isSome()); if (key.isSome()) { aBuilder.PushText(aBounds, aClip, aBackfaceVisible, aColor, key.value(), aGlyphs, aGlyphOptions); } } Maybe WebRenderBridgeChild::GetFontKeyForScaledFont( gfx::ScaledFont* aScaledFont, wr::IpcResourceUpdateQueue* aResources) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(aScaledFont); MOZ_ASSERT(aScaledFont->CanSerialize()); return mFontInstanceKeys.WithEntryHandle( aScaledFont, [&](auto&& entry) -> Maybe { if (!entry) { Maybe resources = aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this)); aResources = resources.ptrOr(aResources); Maybe fontKey = GetFontKeyForUnscaledFont( aScaledFont->GetUnscaledFont(), aResources); if (fontKey.isNothing()) { return Nothing(); } wr::FontInstanceKey instanceKey = GetNextFontInstanceKey(); Maybe options; Maybe platformOptions; std::vector variations; aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions, &variations); aResources->AddFontInstance( instanceKey, fontKey.value(), aScaledFont->GetSize(), options.ptrOr(nullptr), platformOptions.ptrOr(nullptr), Range(variations.data(), variations.size())); if (resources.isSome()) { UpdateResources(resources.ref()); } entry.Insert(instanceKey); } return Some(*entry); }); } Maybe WebRenderBridgeChild::GetFontKeyForUnscaledFont( gfx::UnscaledFont* aUnscaled, wr::IpcResourceUpdateQueue* aResources) { MOZ_ASSERT(!mDestroyed); return mFontKeys.WithEntryHandle( aUnscaled, [&](auto&& entry) -> Maybe { if (!entry) { Maybe resources = aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this)); wr::FontKey fontKey = {wr::IdNamespace{0}, 0}; FontFileDataSink sink = {&fontKey, this, resources.ptrOr(aResources)}; // First try to retrieve a descriptor for the font, as this is much // cheaper to send over IPC than the full raw font data. If this is // not possible, then and only then fall back to getting the raw font // file data. If that fails, then the only thing left to do is signal // failure by returning a null font key. if (!aUnscaled->GetFontDescriptor(WriteFontDescriptor, &sink) && !aUnscaled->GetFontFileData(WriteFontFileData, &sink)) { return Nothing(); } if (resources.isSome()) { UpdateResources(resources.ref()); } entry.Insert(fontKey); } return Some(*entry); }); } void WebRenderBridgeChild::RemoveExpiredFontKeys( wr::IpcResourceUpdateQueue& aResourceUpdates) { uint32_t counter = gfx::ScaledFont::DeletionCounter(); if (mFontInstanceKeysDeleted != counter) { mFontInstanceKeysDeleted = counter; for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) { if (!iter.Key()) { aResourceUpdates.DeleteFontInstance(iter.Data()); iter.Remove(); } } } counter = gfx::UnscaledFont::DeletionCounter(); if (mFontKeysDeleted != counter) { mFontKeysDeleted = counter; for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) { if (!iter.Key()) { aResourceUpdates.DeleteFont(iter.Data()); iter.Remove(); } } } } CompositorBridgeChild* WebRenderBridgeChild::GetCompositorBridgeChild() { if (!IPCOpen()) { return nullptr; } return static_cast(Manager()); } TextureForwarder* WebRenderBridgeChild::GetTextureForwarder() { return static_cast(GetCompositorBridgeChild()); } LayersIPCActor* WebRenderBridgeChild::GetLayersIPCActor() { return static_cast(GetCompositorBridgeChild()); } void WebRenderBridgeChild::SyncWithCompositor() { if (!IPCOpen()) { return; } SendSyncWithCompositor(); } void WebRenderBridgeChild::Connect(CompositableClient* aCompositable, ImageContainer* aImageContainer) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(aCompositable); static uint64_t sNextID = 1; uint64_t id = sNextID++; mCompositables.InsertOrUpdate(id, aCompositable); CompositableHandle handle(id); aCompositable->InitIPDL(handle); SendNewCompositable(handle, aCompositable->GetTextureInfo()); } void WebRenderBridgeChild::UseTiledLayerBuffer( CompositableClient* aCompositable, const SurfaceDescriptorTiles& aTiledDescriptor) {} void WebRenderBridgeChild::UpdateTextureRegion( CompositableClient* aCompositable, const ThebesBufferData& aThebesBufferData, const nsIntRegion& aUpdatedRegion) {} bool WebRenderBridgeChild::AddOpDestroy(const OpDestroy& aOp) { if (!mIsInTransaction) { return false; } mDestroyedActors.AppendElement(aOp); return true; } void WebRenderBridgeChild::ReleaseCompositable( const CompositableHandle& aHandle) { if (!IPCOpen()) { // This can happen if the IPC connection was torn down, because, e.g. // the GPU process died. return; } if (!DestroyInTransaction(aHandle)) { SendReleaseCompositable(aHandle); } mCompositables.Remove(aHandle.Value()); } bool WebRenderBridgeChild::DestroyInTransaction(PTextureChild* aTexture) { return AddOpDestroy(OpDestroy(aTexture)); } bool WebRenderBridgeChild::DestroyInTransaction( const CompositableHandle& aHandle) { return AddOpDestroy(OpDestroy(aHandle)); } void WebRenderBridgeChild::RemoveTextureFromCompositable( CompositableClient* aCompositable, TextureClient* aTexture) { MOZ_ASSERT(aCompositable); MOZ_ASSERT(aTexture); MOZ_ASSERT(aTexture->GetIPDLActor()); MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == GetIPCChannel()); if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) { // We don't have an actor anymore, don't try to use it! return; } AddWebRenderParentCommand(CompositableOperation( aCompositable->GetIPCHandle(), OpRemoveTexture(nullptr, aTexture->GetIPDLActor()))); } void WebRenderBridgeChild::UseTextures( CompositableClient* aCompositable, const nsTArray& aTextures) { MOZ_ASSERT(aCompositable); if (!aCompositable->IsConnected()) { return; } AutoTArray textures; for (auto& t : aTextures) { MOZ_ASSERT(t.mTextureClient); MOZ_ASSERT(t.mTextureClient->GetIPDLActor()); MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == GetIPCChannel()); bool readLocked = t.mTextureClient->OnForwardedToHost(); textures.AppendElement( TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), t.mTimeStamp, t.mPictureRect, t.mFrameID, t.mProducerID, readLocked)); GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary( t.mTextureClient); auto fenceFd = t.mTextureClient->GetInternalData()->GetAcquireFence(); if (fenceFd.IsValid()) { AddWebRenderParentCommand(CompositableOperation( aCompositable->GetIPCHandle(), OpDeliverAcquireFence(nullptr, t.mTextureClient->GetIPDLActor(), fenceFd))); } } AddWebRenderParentCommand(CompositableOperation(aCompositable->GetIPCHandle(), OpUseTexture(textures))); } void WebRenderBridgeChild::UseComponentAlphaTextures( CompositableClient* aCompositable, TextureClient* aClientOnBlack, TextureClient* aClientOnWhite) {} void WebRenderBridgeChild::UpdateFwdTransactionId() { GetCompositorBridgeChild()->UpdateFwdTransactionId(); } uint64_t WebRenderBridgeChild::GetFwdTransactionId() { return GetCompositorBridgeChild()->GetFwdTransactionId(); } bool WebRenderBridgeChild::InForwarderThread() { return NS_IsMainThread(); } mozilla::ipc::IPCResult WebRenderBridgeChild::RecvWrUpdated( const wr::IdNamespace& aNewIdNamespace, const TextureFactoryIdentifier& textureFactoryIdentifier) { if (mManager) { mManager->WrUpdated(); } IdentifyTextureHost(textureFactoryIdentifier); // Update mIdNamespace to identify obsolete keys and messages by // WebRenderBridgeParent. Since usage of invalid keys could cause crash in // webrender. mIdNamespace = aNewIdNamespace; // Just clear FontInstaceKeys/FontKeys, they are removed during WebRenderAPI // destruction. mFontInstanceKeys.Clear(); mFontKeys.Clear(); return IPC_OK(); } mozilla::ipc::IPCResult WebRenderBridgeChild::RecvWrReleasedImages( nsTArray&& aPairs) { if (mManager) { mManager->WrReleasedImages(aPairs); } return IPC_OK(); } void WebRenderBridgeChild::BeginClearCachedResources() { mSentDisplayList = false; mIsInClearCachedResources = true; // Clear display list and animtaions at parent side before clearing cached // resources on client side. It prevents to clear resources before clearing // display list at parent side. SendClearCachedResources(); } void WebRenderBridgeChild::EndClearCachedResources() { if (!IPCOpen()) { mIsInClearCachedResources = false; return; } ProcessWebRenderParentCommands(); mIsInClearCachedResources = false; } void WebRenderBridgeChild::SetWebRenderLayerManager( WebRenderLayerManager* aManager) { MOZ_ASSERT(aManager && !mManager); mManager = aManager; MOZ_ASSERT(NS_IsMainThread() || !XRE_IsContentProcess()); mActiveResourceTracker = MakeUnique(1000, "CompositableForwarder", nullptr); } ipc::IShmemAllocator* WebRenderBridgeChild::GetShmemAllocator() { if (!IPCOpen()) { return nullptr; } return static_cast(Manager()); } RefPtr WebRenderBridgeChild::GetForMedia() { MOZ_ASSERT(NS_IsMainThread()); // Ensure device initialization for video playback unless they are all remote. // The devices are lazily initialized with WebRender to reduce memory usage. if (!PDMFactory::AllDecodersAreRemote()) { gfxPlatform::GetPlatform()->EnsureDevicesInitialized(); } return MakeAndAddRef( GetTextureFactoryIdentifier()); } bool WebRenderBridgeChild::AllocResourceShmem(size_t aSize, RefCountedShmem& aShm) { // We keep a single shmem around to reuse later if it is reference count has // dropped back to 1 (the reference held by the WebRenderBridgeChild). // If the cached shmem exists, has the correct size and isn't held by anything // other than us, recycle it. bool alreadyAllocated = RefCountedShm::IsValid(mResourceShm); if (alreadyAllocated) { if (RefCountedShm::GetSize(mResourceShm) == aSize && RefCountedShm::GetReferenceCount(mResourceShm) <= 1) { MOZ_ASSERT(RefCountedShm::GetReferenceCount(mResourceShm) == 1); aShm = mResourceShm; return true; } } // If there was no cached shmem or we couldn't recycle it, alloc a new one. if (!RefCountedShm::Alloc(this, aSize, aShm)) { return false; } // Now that we have a valid shmem, put it in the cache if we don't have one // yet. if (!alreadyAllocated) { mResourceShm = aShm; RefCountedShm::AddRef(aShm); } return true; } void WebRenderBridgeChild::DeallocResourceShmem(RefCountedShmem& aShm) { if (!RefCountedShm::IsValid(aShm)) { return; } MOZ_ASSERT(RefCountedShm::GetReferenceCount(aShm) == 0); RefCountedShm::Dealloc(this, aShm); } void WebRenderBridgeChild::Capture() { this->SendCapture(); } void WebRenderBridgeChild::StartCaptureSequence(const nsCString& aPath, uint32_t aFlags) { this->SendStartCaptureSequence(aPath, aFlags); } void WebRenderBridgeChild::StopCaptureSequence() { this->SendStopCaptureSequence(); } } // namespace layers } // namespace mozilla