gecko-dev/gfx/layers/wr/WebRenderBridgeChild.cpp

661 строка
19 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 "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)
: 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()
{
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<OpUpdateResource> resourceUpdates;
nsTArray<RefCountedShmem> smallShmems;
nsTArray<ipc::Shmem> largeShmems;
aResources.Flush(resourceUpdates, smallShmems, largeShmems);
this->SendUpdateResources(resourceUpdates, Move(smallShmems), largeShmems);
}
void
WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aContentSize,
wr::BuiltDisplayList& aDL,
wr::IpcResourceUpdateQueue& aResources,
const gfx::IntSize& aSize,
TransactionId 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<OpUpdateResource> resourceUpdates;
nsTArray<RefCountedShmem> smallShmems;
nsTArray<ipc::Shmem> largeShmems;
aResources.Flush(resourceUpdates, smallShmems, largeShmems);
this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
GetFwdTransactionId(), aTransactionId,
aContentSize, dlData, aDL.dl_desc, aScrollData,
Move(resourceUpdates), Move(smallShmems), largeShmems,
mIdNamespace, aTxnStartTime, fwdTime);
mParentCommands.Clear();
mDestroyedActors.Clear();
mIsInTransaction = false;
}
void
WebRenderBridgeChild::EndEmptyTransaction(const FocusTarget& aFocusTarget,
TransactionId 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)
{
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();
}
wr::ExternalImageId
WebRenderBridgeChild::AllocExternalImageIdForCompositable(CompositableClient* aCompositable)
{
wr::ExternalImageId imageId = GetNextExternalImageId();
AddWebRenderParentCommand(
OpAddExternalImageIdForCompositable(imageId, aCompositable->GetIPCHandle()));
return imageId;
}
void
WebRenderBridgeChild::DeallocExternalImageId(const wr::ExternalImageId& aImageId)
{
AddWebRenderParentCommand(
OpRemoveExternalImageId(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<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, 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);
wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
aBuilder.PushText(aBounds,
aClip,
aBackfaceVisible,
aColor,
key,
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(this);
wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont());
wr::FontKey nullKey = { wr::IdNamespace { 0 }, 0};
if (fontKey == nullKey) {
return instanceKey;
}
instanceKey = GetNextFontInstanceKey();
Maybe<wr::FontInstanceOptions> options;
Maybe<wr::FontInstancePlatformOptions> platformOptions;
std::vector<FontVariation> variations;
aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions, &variations);
resources.AddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(),
options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
Range<const FontVariation>(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(this);
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(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()
{
return static_cast<CompositorBridgeChild*>(Manager());
}
TextureForwarder*
WebRenderBridgeChild::GetTextureForwarder()
{
return static_cast<TextureForwarder*>(GetCompositorBridgeChild());
}
LayersIPCActor*
WebRenderBridgeChild::GetLayersIPCActor()
{
return static_cast<LayersIPCActor*>(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<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(nullptr, t.mTextureClient->GetIPDLActor(),
t.mTimeStamp, t.mPictureRect,
t.mFrameID, t.mProducerID,
readLocked));
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;
// 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;
nsCOMPtr<nsIEventTarget> eventTarget = nullptr;
if (dom::TabGroup* tabGroup = mManager->GetTabGroup()) {
eventTarget = tabGroup->EventTargetFor(TaskCategory::Other);
}
MOZ_ASSERT(eventTarget || !XRE_IsContentProcess());
mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(
1000, "CompositableForwarder", eventTarget);
}
ipc::IShmemAllocator*
WebRenderBridgeChild::GetShmemAllocator()
{
return static_cast<CompositorBridgeChild*>(Manager());
}
RefPtr<KnowsCompositor>
WebRenderBridgeChild::GetForMedia()
{
MOZ_ASSERT(NS_IsMainThread());
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();
}
} // namespace layers
} // namespace mozilla