diff --git a/gfx/layers/wr/WebRenderBridgeChild.cpp b/gfx/layers/wr/WebRenderBridgeChild.cpp --- a/gfx/layers/wr/WebRenderBridgeChild.cpp +++ b/gfx/layers/wr/WebRenderBridgeChild.cpp @@ -1,634 +1,633 @@ /* -*- 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/dom/TabGroup.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" namespace mozilla { namespace layers { using namespace mozilla::gfx; WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId) : mReadLockSequenceNumber(0) , mIsInTransaction(false) , mIsInClearCachedResources(false) , mIdNamespace{0} , mResourceId(0) , mPipelineId(aPipelineId) , mManager(nullptr) , mIPCOpen(false) , mDestroyed(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() { // 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) { MOZ_ASSERT(mIsInTransaction || mIsInClearCachedResources); mParentCommands.AppendElement(aCmd); } void WebRenderBridgeChild::AddWebRenderParentCommands(const nsTArray& aCommands) { MOZ_ASSERT(mIsInTransaction); mParentCommands.AppendElements(aCommands); } void WebRenderBridgeChild::BeginTransaction() { MOZ_ASSERT(!mDestroyed); UpdateFwdTransactionId(); mIsInTransaction = true; mReadLockSequenceNumber = 0; mReadLocks.AppendElement(); } void WebRenderBridgeChild::ClearReadLocks() { for (nsTArray& locks : mReadLocks) { if (locks.Length()) { if (!SendInitReadLocks(locks)) { NS_WARNING("WARNING: sending read locks failed!"); return; } } } mReadLocks.Clear(); } void WebRenderBridgeChild::UpdateResources(wr::IpcResourceUpdateQueue& aResources) { if (!IPCOpen()) { aResources.Clear(); return; } nsTArray resourceUpdates; nsTArray smallShmems; nsTArray largeShmems; aResources.Flush(resourceUpdates, smallShmems, largeShmems); this->SendUpdateResources(resourceUpdates, Move(smallShmems), Move(largeShmems)); } void WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aContentSize, wr::BuiltDisplayList& aDL, wr::IpcResourceUpdateQueue& aResources, const gfx::IntSize& aSize, uint64_t aTransactionId, const WebRenderScrollData& aScrollData, const mozilla::TimeStamp& aTxnStartTime) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(mIsInTransaction); ByteBuf dlData(aDL.dl.inner.data, aDL.dl.inner.length, aDL.dl.inner.capacity); aDL.dl.inner.capacity = 0; aDL.dl.inner.data = nullptr; TimeStamp fwdTime; #if defined(ENABLE_FRAME_LATENCY_LOG) fwdTime = TimeStamp::Now(); #endif nsTArray resourceUpdates; nsTArray smallShmems; nsTArray largeShmems; aResources.Flush(resourceUpdates, smallShmems, largeShmems); this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId, aContentSize, dlData, aDL.dl_desc, aScrollData, Move(resourceUpdates), Move(smallShmems), Move(largeShmems), mIdNamespace, aTxnStartTime, fwdTime); mParentCommands.Clear(); mDestroyedActors.Clear(); mIsInTransaction = false; } void WebRenderBridgeChild::EndEmptyTransaction(const FocusTarget& aFocusTarget, uint64_t aTransactionId, const mozilla::TimeStamp& aTxnStartTime) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(mIsInTransaction); TimeStamp fwdTime; #if defined(ENABLE_FRAME_LATENCY_LOG) fwdTime = TimeStamp::Now(); #endif this->SendEmptyTransaction(aFocusTarget, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId, mIdNamespace, aTxnStartTime, fwdTime); mParentCommands.Clear(); mDestroyedActors.Clear(); mIsInTransaction = false; } void WebRenderBridgeChild::ProcessWebRenderParentCommands() { MOZ_ASSERT(!mDestroyed); if (mParentCommands.IsEmpty()) { return; } this->SendParentCommands(mParentCommands); mParentCommands.Clear(); } void WebRenderBridgeChild::AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) { MOZ_ASSERT(!mDestroyed); SendAddPipelineIdForCompositable(aPipelineId, aHandle, true); } void WebRenderBridgeChild::AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) { MOZ_ASSERT(!mDestroyed); SendAddPipelineIdForCompositable(aPipelineId, aHandle, false); } void WebRenderBridgeChild::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId) { if (!IPCOpen()) { return; } SendRemovePipelineIdForCompositable(aPipelineId); } wr::ExternalImageId WebRenderBridgeChild::GetNextExternalImageId() { wr::MaybeExternalImageId id = GetCompositorBridgeChild()->GetNextExternalImageId(); MOZ_RELEASE_ASSERT(id.isSome()); return id.value(); } wr::ExternalImageId WebRenderBridgeChild::AllocExternalImageIdForCompositable(CompositableClient* aCompositable) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(aCompositable->IsConnected()); wr::ExternalImageId imageId = GetNextExternalImageId(); SendAddExternalImageIdForCompositable(imageId, aCompositable->GetIPCHandle()); return imageId; } void WebRenderBridgeChild::DeallocExternalImageId(const wr::ExternalImageId& aImageId) { if (mDestroyed) { // This can happen if the IPC connection was torn down, because, e.g. // the GPU process died. return; } SendRemoveExternalImageId(aImageId); } 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, const nsTArray& aGlyphs, +WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, Range aGlyphs, gfx::ScaledFont* aFont, const wr::ColorF& aColor, const StackingContextHelper& aSc, const wr::LayerRect& aBounds, const wr::LayerRect& aClip, bool aBackfaceVisible, const wr::GlyphOptions* aGlyphOptions) { MOZ_ASSERT(aFont); - MOZ_ASSERT(!aGlyphs.IsEmpty()); wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont); MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle); aBuilder.PushText(aBounds, aClip, aBackfaceVisible, aColor, key, - Range(aGlyphs.Elements(), aGlyphs.Length()), + aGlyphs, aGlyphOptions); } wr::FontInstanceKey WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(aScaledFont); MOZ_ASSERT((aScaledFont->GetType() == gfx::FontType::DWRITE) || (aScaledFont->GetType() == gfx::FontType::MAC) || (aScaledFont->GetType() == gfx::FontType::FONTCONFIG)); wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 }; if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) { return instanceKey; } wr::IpcResourceUpdateQueue resources(GetShmemAllocator()); wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont()); wr::FontKey nullKey = { wr::IdNamespace { 0 }, 0}; if (fontKey == nullKey) { return instanceKey; } instanceKey = GetNextFontInstanceKey(); Maybe options; Maybe platformOptions; std::vector variations; aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions, &variations); resources.AddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(), options.ptrOr(nullptr), platformOptions.ptrOr(nullptr), Range(variations.data(), variations.size())); UpdateResources(resources); mFontInstanceKeys.Put(aScaledFont, instanceKey); return instanceKey; } wr::FontKey WebRenderBridgeChild::GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaled) { MOZ_ASSERT(!mDestroyed); wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0}; if (!mFontKeys.Get(aUnscaled, &fontKey)) { wr::IpcResourceUpdateQueue resources(GetShmemAllocator()); FontFileDataSink sink = { &fontKey, this, &resources }; // 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->GetWRFontDescriptor(WriteFontDescriptor, &sink) && !aUnscaled->GetFontFileData(WriteFontFileData, &sink)) { return fontKey; } UpdateResources(resources); mFontKeys.Put(aUnscaled, fontKey); } return fontKey; } void WebRenderBridgeChild::RemoveExpiredFontKeys() { uint32_t counter = gfx::ScaledFont::DeletionCounter(); wr::IpcResourceUpdateQueue resources(GetShmemAllocator()); if (mFontInstanceKeysDeleted != counter) { mFontInstanceKeysDeleted = counter; for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) { if (!iter.Key()) { resources.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()) { resources.DeleteFont(iter.Data()); iter.Remove(); } } } UpdateResources(resources); } CompositorBridgeChild* WebRenderBridgeChild::GetCompositorBridgeChild() { return static_cast(Manager()); } TextureForwarder* WebRenderBridgeChild::GetTextureForwarder() { return static_cast(GetCompositorBridgeChild()); } LayersIPCActor* WebRenderBridgeChild::GetLayersIPCActor() { return static_cast(GetCompositorBridgeChild()); } void WebRenderBridgeChild::SyncWithCompositor() { auto compositorBridge = GetCompositorBridgeChild(); if (compositorBridge && compositorBridge->IPCOpen()) { compositorBridge->SendSyncWithCompositor(); } } void WebRenderBridgeChild::Connect(CompositableClient* aCompositable, ImageContainer* aImageContainer) { MOZ_ASSERT(!mDestroyed); MOZ_ASSERT(aCompositable); static uint64_t sNextID = 1; uint64_t id = sNextID++; mCompositables.Put(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()); ReadLockDescriptor readLock; ReadLockHandle readLockHandle; if (t.mTextureClient->SerializeReadLock(readLock)) { readLockHandle = ReadLockHandle(++mReadLockSequenceNumber); if (mReadLocks.LastElement().Length() >= GetMaxFileDescriptorsPerMessage()) { mReadLocks.AppendElement(); } mReadLocks.LastElement().AppendElement(ReadLockInit(readLock, readLockHandle)); } textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(), readLockHandle, t.mTimeStamp, t.mPictureRect, t.mFrameID, t.mProducerID)); GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient); } 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) { if (mManager) { mManager->WrUpdated(); } // 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(); } void WebRenderBridgeChild::BeginClearCachedResources() { mIsInClearCachedResources = true; } void WebRenderBridgeChild::EndClearCachedResources() { if (!IPCOpen()) { mIsInClearCachedResources = false; return; } ProcessWebRenderParentCommands(); SendClearCachedResources(); mIsInClearCachedResources = false; } void WebRenderBridgeChild::SetWebRenderLayerManager(WebRenderLayerManager* aManager) { MOZ_ASSERT(aManager && !mManager); mManager = aManager; nsCOMPtr eventTarget = nullptr; if (dom::TabGroup* tabGroup = mManager->GetTabGroup()) { eventTarget = tabGroup->EventTargetFor(TaskCategory::Other); } MOZ_ASSERT(eventTarget || !XRE_IsContentProcess()); mActiveResourceTracker = MakeUnique( 1000, "CompositableForwarder", eventTarget); } ipc::IShmemAllocator* WebRenderBridgeChild::GetShmemAllocator() { return static_cast(Manager()); } } // namespace layers } // namespace mozilla diff --git a/gfx/layers/wr/WebRenderBridgeChild.h b/gfx/layers/wr/WebRenderBridgeChild.h --- a/gfx/layers/wr/WebRenderBridgeChild.h +++ b/gfx/layers/wr/WebRenderBridgeChild.h @@ -1,228 +1,228 @@ /* -*- 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/. */ #ifndef mozilla_layers_WebRenderBridgeChild_h #define mozilla_layers_WebRenderBridgeChild_h #include "mozilla/layers/CompositableForwarder.h" #include "mozilla/layers/PWebRenderBridgeChild.h" namespace mozilla { namespace widget { class CompositorWidget; } namespace wr { class DisplayListBuilder; class ResourceUpdateQueue; class IpcResourceUpdateQueue; } namespace layers { class CompositableClient; class CompositorBridgeChild; class StackingContextHelper; class TextureForwarder; class WebRenderLayerManager; template class ThreadSafeWeakPtrHashKey : public PLDHashEntryHdr { public: typedef RefPtr KeyType; typedef const T* KeyTypePointer; explicit ThreadSafeWeakPtrHashKey(KeyTypePointer aKey) : mKey(do_AddRef(const_cast(aKey))) {} KeyType GetKey() const { return do_AddRef(mKey); } bool KeyEquals(KeyTypePointer aKey) const { return mKey == aKey; } static KeyTypePointer KeyToPointer(const KeyType& aKey) { return aKey.get(); } static PLDHashNumber HashKey(KeyTypePointer aKey) { return NS_PTR_TO_UINT32(aKey) >> 2; } enum { ALLOW_MEMMOVE = true }; private: ThreadSafeWeakPtr mKey; }; typedef ThreadSafeWeakPtrHashKey UnscaledFontHashKey; typedef ThreadSafeWeakPtrHashKey ScaledFontHashKey; class WebRenderBridgeChild final : public PWebRenderBridgeChild , public CompositableForwarder { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override) public: explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId); void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd); void AddWebRenderParentCommands(const nsTArray& aCommands); void UpdateResources(wr::IpcResourceUpdateQueue& aResources); void BeginTransaction(); void EndTransaction(const wr::LayoutSize& aContentSize, wr::BuiltDisplayList& dl, wr::IpcResourceUpdateQueue& aResources, const gfx::IntSize& aSize, uint64_t aTransactionId, const WebRenderScrollData& aScrollData, const mozilla::TimeStamp& aTxnStartTime); void EndEmptyTransaction(const FocusTarget& aFocusTarget, uint64_t aTransactionId, const mozilla::TimeStamp& aTxnStartTime); void ProcessWebRenderParentCommands(); CompositorBridgeChild* GetCompositorBridgeChild(); wr::PipelineId GetPipeline() { return mPipelineId; } // KnowsCompositor TextureForwarder* GetTextureForwarder() override; LayersIPCActor* GetLayersIPCActor() override; void SyncWithCompositor() override; ActiveResourceTracker* GetActiveResourceTracker() override { return mActiveResourceTracker.get(); } void AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId, const CompositableHandle& aHandlee); void AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId, const CompositableHandle& aHandlee); void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId); wr::ExternalImageId AllocExternalImageIdForCompositable(CompositableClient* aCompositable); void DeallocExternalImageId(const wr::ExternalImageId& aImageId); /** * Clean this up, finishing with SendShutDown() which will cause __delete__ * to be sent from the parent side. */ void Destroy(bool aIsSync); bool IPCOpen() const { return mIPCOpen && !mDestroyed; } bool IsDestroyed() const { return mDestroyed; } uint32_t GetNextResourceId() { return ++mResourceId; } wr::IdNamespace GetNamespace() { return mIdNamespace; } void SetNamespace(wr::IdNamespace aIdNamespace) { mIdNamespace = aIdNamespace; } wr::FontKey GetNextFontKey() { return wr::FontKey { GetNamespace(), GetNextResourceId() }; } wr::FontInstanceKey GetNextFontInstanceKey() { return wr::FontInstanceKey { GetNamespace(), GetNextResourceId() }; } wr::WrImageKey GetNextImageKey() { return wr::WrImageKey{ GetNamespace(), GetNextResourceId() }; } - void PushGlyphs(wr::DisplayListBuilder& aBuilder, const nsTArray& aGlyphs, + void PushGlyphs(wr::DisplayListBuilder& aBuilder, Range aGlyphs, gfx::ScaledFont* aFont, const wr::ColorF& aColor, const StackingContextHelper& aSc, const wr::LayerRect& aBounds, const wr::LayerRect& aClip, bool aBackfaceVisible, const wr::GlyphOptions* aGlyphOptions = nullptr); wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont); wr::FontKey GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaledFont); void RemoveExpiredFontKeys(); void ClearReadLocks(); void BeginClearCachedResources(); void EndClearCachedResources(); void SetWebRenderLayerManager(WebRenderLayerManager* aManager); ipc::IShmemAllocator* GetShmemAllocator(); private: friend class CompositorBridgeChild; ~WebRenderBridgeChild(); wr::ExternalImageId GetNextExternalImageId(); // CompositableForwarder void Connect(CompositableClient* aCompositable, ImageContainer* aImageContainer = nullptr) override; void UseTiledLayerBuffer(CompositableClient* aCompositable, const SurfaceDescriptorTiles& aTiledDescriptor) override; void UpdateTextureRegion(CompositableClient* aCompositable, const ThebesBufferData& aThebesBufferData, const nsIntRegion& aUpdatedRegion) override; void ReleaseCompositable(const CompositableHandle& aHandle) override; bool DestroyInTransaction(PTextureChild* aTexture) override; bool DestroyInTransaction(const CompositableHandle& aHandle); void RemoveTextureFromCompositable(CompositableClient* aCompositable, TextureClient* aTexture) override; void UseTextures(CompositableClient* aCompositable, const nsTArray& aTextures) override; void UseComponentAlphaTextures(CompositableClient* aCompositable, TextureClient* aClientOnBlack, TextureClient* aClientOnWhite) override; void UpdateFwdTransactionId() override; uint64_t GetFwdTransactionId() override; bool InForwarderThread() override; void ActorDestroy(ActorDestroyReason why) override; void DoDestroy(); mozilla::ipc::IPCResult RecvWrUpdated(const wr::IdNamespace& aNewIdNamespace) override; void AddIPDLReference() { MOZ_ASSERT(mIPCOpen == false); mIPCOpen = true; AddRef(); } void ReleaseIPDLReference() { MOZ_ASSERT(mIPCOpen == true); mIPCOpen = false; Release(); } bool AddOpDestroy(const OpDestroy& aOp); nsTArray mParentCommands; nsTArray mDestroyedActors; nsDataHashtable mCompositables; nsTArray> mReadLocks; uint64_t mReadLockSequenceNumber; bool mIsInTransaction; bool mIsInClearCachedResources; wr::IdNamespace mIdNamespace; uint32_t mResourceId; wr::PipelineId mPipelineId; WebRenderLayerManager* mManager; bool mIPCOpen; bool mDestroyed; uint32_t mFontKeysDeleted; nsDataHashtable mFontKeys; uint32_t mFontInstanceKeysDeleted; nsDataHashtable mFontInstanceKeys; UniquePtr mActiveResourceTracker; }; } // namespace layers } // namespace mozilla #endif // mozilla_layers_WebRenderBridgeChild_h diff --git a/layout/generic/TextDrawTarget.h b/layout/generic/TextDrawTarget.h --- a/layout/generic/TextDrawTarget.h +++ b/layout/generic/TextDrawTarget.h @@ -1,496 +1,504 @@ /* -*- 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/. */ #ifndef TextDrawTarget_h #define TextDrawTarget_h #include "mozilla/gfx/2D.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/webrender/WebRenderAPI.h" #include "mozilla/layers/StackingContextHelper.h" namespace mozilla { namespace layout { using namespace gfx; // This class is fake DrawTarget, used to intercept text draw calls, while // also collecting up the other aspects of text natively. // // When using advanced-layers in nsDisplayText's constructor, we construct this // and run the full painting algorithm with this as the DrawTarget. This is // done to avoid having to massively refactor gecko's text painting code (which // has lots of components shared between other rendering algorithms). // // In some phases of the painting algorithm, we can grab the relevant values // and feed them directly into TextDrawTarget. For instance, selections, // decorations, and shadows are handled in this manner. In those cases we can // also short-circuit the painting algorithm to save work. // // In other phases, the computed values are sufficiently buried in complex // code that it's best for us to just intercept the final draw calls. This // is how we handle computing the glyphs of the main text and text-emphasis // (see our overloaded FillGlyphs implementation). // // To be clear: this is a big hack. With time we hope to refactor the codebase // so that all the elements of text are handled directly by TextDrawTarget, // which is to say everything is done like we do selections and shadows now. // This design is a good step for doing this work incrementally. // // This is also likely to be a bit buggy (missing or misinterpreted info) // while we further develop the design. // // TextDrawTarget doesn't yet support all features. See mHasUnsupportedFeatures // for details. class TextDrawTarget : public DrawTarget { public: explicit TextDrawTarget(wr::DisplayListBuilder& aBuilder, const layers::StackingContextHelper& aSc, layers::WebRenderLayerManager* aManager, nsDisplayItem* aItem, nsRect& aBounds) : mBuilder(aBuilder), mSc(aSc), mManager(aManager) { SetPermitSubpixelAA(!aItem->IsSubpixelAADisabled()); // Compute clip/bounds auto appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); LayoutDeviceRect layoutBoundsRect = LayoutDeviceRect::FromAppUnits( aBounds, appUnitsPerDevPixel); LayoutDeviceRect layoutClipRect = layoutBoundsRect; auto clip = aItem->GetClip(); if (clip.HasClip()) { layoutClipRect = LayoutDeviceRect::FromAppUnits( clip.GetClipRect(), appUnitsPerDevPixel); } mBoundsRect = aSc.ToRelativeLayoutRect(layoutBoundsRect); mClipRect = aSc.ToRelativeLayoutRect(layoutClipRect); mBackfaceVisible = !aItem->BackfaceIsHidden(); mBuilder.Save(); } // Prevent this from being copied TextDrawTarget(const TextDrawTarget& src) = delete; TextDrawTarget& operator=(const TextDrawTarget&) = delete; ~TextDrawTarget() { if (mHasUnsupportedFeatures) { mBuilder.Restore(); } else { mBuilder.ClearSave(); } } void FoundUnsupportedFeature() { mHasUnsupportedFeatures = true; } bool HasUnsupportedFeatures() { return mHasUnsupportedFeatures; } // This overload just stores the glyphs/font/color. void FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern, const DrawOptions& aOptions) override { // Make sure we're only given boring color patterns MOZ_RELEASE_ASSERT(aOptions.mCompositionOp == CompositionOp::OP_OVER); MOZ_RELEASE_ASSERT(aOptions.mAlpha == 1.0f); MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR); - auto* colorPat = static_cast(&aPattern); - auto color = wr::ToColorF(colorPat->mColor); // Make sure the font exists, and can be serialized MOZ_RELEASE_ASSERT(aFont); if (!aFont->CanSerialize()) { FoundUnsupportedFeature(); return; } - // 170 is the maximum size gfxFont is expected to hand us - AutoTArray glyphs; - glyphs.SetLength(aBuffer.mNumGlyphs); - - for (size_t i = 0; i < aBuffer.mNumGlyphs; i++) { - wr::GlyphInstance& targetGlyph = glyphs[i]; - const gfx::Glyph& sourceGlyph = aBuffer.mGlyphs[i]; - targetGlyph.index = sourceGlyph.mIndex; - targetGlyph.point = mSc.ToRelativeLayoutPoint( - LayoutDevicePoint::FromUnknownPoint(sourceGlyph.mPosition)); - } + auto* colorPat = static_cast(&aPattern); + auto color = wr::ToColorF(colorPat->mColor); + MOZ_ASSERT(aBuffer.mNumGlyphs); + auto glyphs = Range(reinterpret_cast(aBuffer.mGlyphs), aBuffer.mNumGlyphs); + // MSVC won't let us use offsetof on the following directly so we give it a + // name with typedef + typedef std::remove_reference::type GlyphType; + // Compare gfx::Glyph and wr::GlyphInstance to make sure that they are + // structurally equivalent to ensure that our cast above was ok + static_assert(std::is_same() + && std::is_same() + && std::is_same() + && offsetof(GlyphType, mIndex) == offsetof(wr::GlyphInstance, index) + && offsetof(GlyphType, mPosition) == offsetof(wr::GlyphInstance, point) + && offsetof(decltype(aBuffer.mGlyphs[0].mPosition), x) == offsetof(decltype(glyphs[0].point), x) + && offsetof(decltype(aBuffer.mGlyphs[0].mPosition), y) == offsetof(decltype(glyphs[0].point), y) + && std::is_standard_layout>::value + && std::is_standard_layout>::value + && sizeof(aBuffer.mGlyphs[0]) == sizeof(glyphs[0]) + && sizeof(aBuffer.mGlyphs[0].mPosition) == sizeof(glyphs[0].point) + , "glyph buf types don't match"); wr::GlyphOptions glyphOptions; glyphOptions.render_mode = wr::ToFontRenderMode(aOptions.mAntialiasMode, GetPermitSubpixelAA()); mManager->WrBridge()->PushGlyphs(mBuilder, glyphs, aFont, color, mSc, mBoundsRect, mClipRect, mBackfaceVisible, &glyphOptions); } void PushClipRect(const Rect &aRect) override { auto rect = mSc.ToRelativeLayoutRect(LayoutDeviceRect::FromUnknownRect(aRect)); auto clipId = mBuilder.DefineClip(Nothing(), Nothing(), rect); mBuilder.PushClip(clipId); } void PopClip() override { mBuilder.PopClip(); } void AppendShadow(const wr::Shadow& aShadow) { mBuilder.PushShadow(mBoundsRect, mClipRect, mBackfaceVisible, aShadow); mHasShadows = true; } void TerminateShadows() { if (mHasShadows) { mBuilder.PopAllShadows(); mHasShadows = false; } } void AppendSelectionRect(const LayoutDeviceRect& aRect, const Color& aColor) { auto rect = wr::ToLayoutRect(aRect); auto color = wr::ToColorF(aColor); mBuilder.PushRect(rect, mClipRect, mBackfaceVisible, color); } // This function is basically designed to slide into the decoration drawing // code of nsCSSRendering with minimum disruption, to minimize the // chances of implementation drift. As such, it mostly looks like a call // to a skia-style StrokeLine method: two end-points, with a thickness // and style. Notably the end-points are *centered* in the block direction, // even though webrender wants a rect-like representation, where the points // are on corners. // // So we mangle the format here in a single centralized place, where neither // webrender nor nsCSSRendering has to care about this mismatch. // // NOTE: we assume the points are axis-aligned, and aStart should be used // as the top-left corner of the rect. void AppendDecoration(const Point& aStart, const Point& aEnd, const float aThickness, const bool aVertical, const Color& aColor, const uint8_t aStyle) { auto pos = LayoutDevicePoint::FromUnknownPoint(aStart); LayoutDeviceSize size; if (aVertical) { pos.x -= aThickness / 2; // adjust from center to corner size = LayoutDeviceSize(aThickness, aEnd.y - aStart.y); } else { pos.y -= aThickness / 2; // adjust from center to corner size = LayoutDeviceSize(aEnd.x - aStart.x, aThickness); } wr::Line decoration; decoration.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(pos, size)); decoration.wavyLineThickness = 0; // dummy value, unused decoration.color = wr::ToColorF(aColor); decoration.orientation = aVertical ? wr::LineOrientation::Vertical : wr::LineOrientation::Horizontal; switch (aStyle) { case NS_STYLE_TEXT_DECORATION_STYLE_SOLID: decoration.style = wr::LineStyle::Solid; break; case NS_STYLE_TEXT_DECORATION_STYLE_DOTTED: decoration.style = wr::LineStyle::Dotted; break; case NS_STYLE_TEXT_DECORATION_STYLE_DASHED: decoration.style = wr::LineStyle::Dashed; break; // Wavy lines should go through AppendWavyDecoration case NS_STYLE_TEXT_DECORATION_STYLE_WAVY: // Double lines should be lowered to two solid lines case NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE: default: MOZ_CRASH("TextDrawTarget received unsupported line style"); } mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration); } // Seperated out from AppendDecoration because Wavy Lines are completely // different, and trying to merge the concept is more of a mess than it's // worth. void AppendWavyDecoration(const Rect& aBounds, const float aThickness, const bool aVertical, const Color& aColor) { wr::Line decoration; decoration.bounds = mSc.ToRelativeLayoutRect( LayoutDeviceRect::FromUnknownRect(aBounds)); decoration.wavyLineThickness = aThickness; decoration.color = wr::ToColorF(aColor); decoration.orientation = aVertical ? wr::LineOrientation::Vertical : wr::LineOrientation::Horizontal; decoration.style = wr::LineStyle::Wavy; mBuilder.PushLine(mClipRect, mBackfaceVisible, decoration); } private: // Whether anything unsupported was encountered. Currently: // // * Synthetic bold/italics // * SVG fonts // * Unserializable fonts // * Tofu glyphs // * Pratial ligatures // * Text writing-mode // * Text stroke bool mHasUnsupportedFeatures = false; // Whether PopAllShadows needs to be called bool mHasShadows = false; // Things used to push to webrender wr::DisplayListBuilder& mBuilder; const layers::StackingContextHelper& mSc; layers::WebRenderLayerManager* mManager; // Computed facts wr::LayerRect mBoundsRect; wr::LayerRect mClipRect; bool mBackfaceVisible; // The rest of this is dummy implementations of DrawTarget's API public: DrawTargetType GetType() const override { return DrawTargetType::SOFTWARE_RASTER; } BackendType GetBackendType() const override { return BackendType::WEBRENDER_TEXT; } bool IsRecording() const override { return true; } bool IsCaptureDT() const override { return false; } already_AddRefed Snapshot() override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed IntoLuminanceSource(LuminanceType aLuminanceType, float aOpacity) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } IntSize GetSize() override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return IntSize(1, 1); } void Flush() override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void DrawCapturedDT(DrawTargetCapture *aCaptureDT, const Matrix& aTransform) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void DrawFilter(FilterNode *aNode, const Rect &aSourceRect, const Point &aDestPoint, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOperator) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void ClearRect(const Rect &aRect) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, const IntPoint &aDestination) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions = DrawOptions()) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void StrokeRect(const Rect &aRect, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void StrokeLine(const Point &aStart, const Point &aEnd, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, const DrawOptions &aOptions) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } bool Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void PushClip(const Path *aPath) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } void PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform, const IntRect& aBounds, bool aCopyBackground) override { // Fine to pretend we do this } void PopLayer() override { // Fine to pretend we do this } already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) const override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed OptimizeSourceSurface(SourceSurface *aSurface) const override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed CreatePathBuilder(FillRule aFillRule) const override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed CreateFilter(FilterType aType) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } already_AddRefed CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } void* GetNativeSurface(NativeSurfaceType aType) override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); return nullptr; } void DetachAllSnapshots() override { MOZ_CRASH("TextDrawTarget: Method shouldn't be called"); } }; } } #endif