summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/WebRenderBridgeChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/wr/WebRenderBridgeChild.cpp')
-rw-r--r--gfx/layers/wr/WebRenderBridgeChild.cpp586
1 files changed, 586 insertions, 0 deletions
diff --git a/gfx/layers/wr/WebRenderBridgeChild.cpp b/gfx/layers/wr/WebRenderBridgeChild.cpp
new file mode 100644
index 0000000000..2c5868007a
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -0,0 +1,586 @@
+/* -*- 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().
+ 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<OpUpdateResource> resourceUpdates;
+ nsTArray<RefCountedShmem> smallShmems;
+ nsTArray<ipc::Shmem> 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<CompositionPayload> 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<TransactionData>&& 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<CompositionPayload> 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(mIdNamespace, mParentCommands);
+ mParentCommands.Clear();
+ }
+}
+
+void WebRenderBridgeChild::AddPipelineIdForCompositable(
+ const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle,
+ CompositableHandleOwner aOwner) {
+ AddWebRenderParentCommand(
+ OpAddPipelineIdForCompositable(aPipelineId, aHandle, aOwner));
+}
+
+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<FontFileDataSink*>(aBaton);
+
+ *sink->mFontKey = sink->mWrBridge->GetNextFontKey();
+
+ sink->mResources->AddRawFont(
+ *sink->mFontKey, Range<uint8_t>(const_cast<uint8_t*>(aData), aLength),
+ aIndex);
+}
+
+static void WriteFontDescriptor(const uint8_t* aData, uint32_t aLength,
+ uint32_t aIndex, void* aBaton) {
+ FontFileDataSink* sink = static_cast<FontFileDataSink*>(aBaton);
+
+ *sink->mFontKey = sink->mWrBridge->GetNextFontKey();
+
+ sink->mResources->AddFontDescriptor(
+ *sink->mFontKey, Range<uint8_t>(const_cast<uint8_t*>(aData), aLength),
+ aIndex);
+}
+
+void WebRenderBridgeChild::PushGlyphs(
+ wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
+ Range<const wr::GlyphInstance> 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<wr::WrFontInstanceKey> key = GetFontKeyForScaledFont(aFont, aResources);
+ MOZ_ASSERT(key.isSome());
+
+ if (key.isSome()) {
+ aBuilder.PushText(aBounds, aClip, aBackfaceVisible, aColor, key.value(),
+ aGlyphs, aGlyphOptions);
+ }
+}
+
+Maybe<wr::FontInstanceKey> 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<wr::FontInstanceKey> {
+ if (!entry) {
+ Maybe<wr::FontKey> fontKey = GetFontKeyForUnscaledFont(
+ aScaledFont->GetUnscaledFont(), aResources);
+ if (fontKey.isNothing()) {
+ return Nothing();
+ }
+
+ wr::FontInstanceKey instanceKey = GetNextFontInstanceKey();
+
+ Maybe<wr::FontInstanceOptions> options;
+ Maybe<wr::FontInstancePlatformOptions> platformOptions;
+ std::vector<FontVariation> variations;
+ aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions,
+ &variations);
+
+ aResources.AddFontInstance(
+ instanceKey, fontKey.value(), aScaledFont->GetSize(),
+ options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
+ Range<const FontVariation>(variations.data(), variations.size()));
+
+ entry.Insert(instanceKey);
+ }
+
+ return Some(*entry);
+ });
+}
+
+Maybe<wr::FontKey> WebRenderBridgeChild::GetFontKeyForUnscaledFont(
+ gfx::UnscaledFont* aUnscaled, wr::IpcResourceUpdateQueue& aResources) {
+ MOZ_ASSERT(!mDestroyed);
+
+ return mFontKeys.WithEntryHandle(
+ aUnscaled, [&](auto&& entry) -> Maybe<wr::FontKey> {
+ if (!entry) {
+ wr::FontKey fontKey = {wr::IdNamespace{0}, 0};
+ FontFileDataSink sink = {&fontKey, this, &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();
+ }
+
+ 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<CompositorBridgeChild*>(Manager());
+}
+
+TextureForwarder* WebRenderBridgeChild::GetTextureForwarder() {
+ return static_cast<TextureForwarder*>(GetCompositorBridgeChild());
+}
+
+LayersIPCActor* WebRenderBridgeChild::GetLayersIPCActor() {
+ return static_cast<LayersIPCActor*>(GetCompositorBridgeChild());
+}
+
+void WebRenderBridgeChild::SyncWithCompositor(
+ const Maybe<uint64_t>& aWindowID) {
+ if (!IPCOpen()) {
+ return;
+ }
+ SendSyncWithCompositor();
+}
+
+void WebRenderBridgeChild::Connect(CompositableClient* aCompositable,
+ ImageContainer* aImageContainer) {
+ MOZ_ASSERT(!mDestroyed);
+ MOZ_ASSERT(aCompositable);
+
+ CompositableHandle handle = CompositableHandle::GetNext();
+ mCompositables.InsertOrUpdate(uint64_t(handle), aCompositable);
+
+ aCompositable->InitIPDL(handle);
+ SendNewCompositable(handle, aCompositable->GetTextureInfo());
+}
+
+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(WrapNotNull(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(WrapNotNull(aTexture->GetIPDLActor()))));
+}
+
+void WebRenderBridgeChild::UseTextures(
+ CompositableClient* aCompositable,
+ const nsTArray<TimedTextureClient>& aTextures) {
+ MOZ_ASSERT(aCompositable);
+
+ if (!aCompositable->IsConnected()) {
+ return;
+ }
+
+ AutoTArray<TimedTexture, 4> 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(
+ WrapNotNull(t.mTextureClient->GetIPDLActor()), t.mTimeStamp,
+ t.mPictureRect, t.mFrameID, t.mProducerID, readLocked));
+ GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(
+ t.mTextureClient);
+ }
+ AddWebRenderParentCommand(CompositableOperation(aCompositable->GetIPCHandle(),
+ OpUseTexture(textures)));
+}
+
+void WebRenderBridgeChild::UseRemoteTexture(
+ CompositableClient* aCompositable, const RemoteTextureId aTextureId,
+ const RemoteTextureOwnerId aOwnerId, const gfx::IntSize aSize,
+ const TextureFlags aFlags, const RefPtr<FwdTransactionTracker>& aTracker) {
+ AddWebRenderParentCommand(CompositableOperation(
+ aCompositable->GetIPCHandle(),
+ OpUseRemoteTexture(aTextureId, aOwnerId, aSize, aFlags)));
+ TrackFwdTransaction(aTracker);
+}
+
+FwdTransactionCounter& WebRenderBridgeChild::GetFwdTransactionCounter() {
+ return GetCompositorBridgeChild()->GetFwdTransactionCounter();
+}
+
+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<wr::ExternalImageKeyPair>&& 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);
+ MOZ_ASSERT(NS_IsMainThread() || !XRE_IsContentProcess());
+ mManager = aManager;
+}
+
+ipc::IShmemAllocator* WebRenderBridgeChild::GetShmemAllocator() {
+ if (!IPCOpen()) {
+ return nullptr;
+ }
+ return static_cast<CompositorBridgeChild*>(Manager());
+}
+
+RefPtr<KnowsCompositor> 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<KnowsCompositorMediaProxy>(
+ 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