/* -*- 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/RemoteTextureMap.h" #include #include #include "CompositableHost.h" #include "mozilla/ipc/ProtocolUtils.h" #include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/AsyncImagePipelineManager.h" #include "mozilla/layers/BufferTexture.h" #include "mozilla/layers/CompositorThread.h" #include "mozilla/layers/ImageDataSerializer.h" #include "mozilla/layers/RemoteTextureHostWrapper.h" #include "mozilla/layers/TextureClientSharedSurface.h" #include "mozilla/layers/WebRenderTextureHost.h" #include "mozilla/StaticPrefs_webgl.h" #include "mozilla/webgpu/ExternalTexture.h" #include "mozilla/webrender/RenderThread.h" #include "SharedSurface.h" namespace mozilla::layers { RemoteTextureRecycleBin::RemoteTextureRecycleBin(bool aIsShared) : mIsShared(aIsShared) {} RemoteTextureRecycleBin::~RemoteTextureRecycleBin() = default; RemoteTextureOwnerClient::RemoteTextureOwnerClient( const base::ProcessId aForPid) : mForPid(aForPid) {} RemoteTextureOwnerClient::~RemoteTextureOwnerClient() = default; bool RemoteTextureOwnerClient::IsRegistered( const RemoteTextureOwnerId aOwnerId) { auto it = mOwnerIds.find(aOwnerId); if (it == mOwnerIds.end()) { return false; } return true; } void RemoteTextureOwnerClient::RegisterTextureOwner( const RemoteTextureOwnerId aOwnerId, bool aSharedRecycling) { MOZ_ASSERT(mOwnerIds.find(aOwnerId) == mOwnerIds.end()); mOwnerIds.emplace(aOwnerId); RefPtr recycleBin; if (aSharedRecycling) { if (!mSharedRecycleBin) { mSharedRecycleBin = new RemoteTextureRecycleBin(true); } recycleBin = mSharedRecycleBin; } RemoteTextureMap::Get()->RegisterTextureOwner(aOwnerId, mForPid, recycleBin); } void RemoteTextureOwnerClient::UnregisterTextureOwner( const RemoteTextureOwnerId aOwnerId) { auto it = mOwnerIds.find(aOwnerId); if (it == mOwnerIds.end()) { return; } mOwnerIds.erase(it); RemoteTextureMap::Get()->UnregisterTextureOwner(aOwnerId, mForPid); } void RemoteTextureOwnerClient::UnregisterAllTextureOwners() { if (mOwnerIds.empty()) { return; } RemoteTextureMap::Get()->UnregisterTextureOwners(mOwnerIds, mForPid); mOwnerIds.clear(); mSharedRecycleBin = nullptr; } bool RemoteTextureOwnerClient::WaitForTxn(const RemoteTextureOwnerId aOwnerId, RemoteTextureTxnType aTxnType, RemoteTextureTxnId aTxnId) { auto it = mOwnerIds.find(aOwnerId); if (it == mOwnerIds.end() || !aTxnType || !aTxnId) { return false; } return RemoteTextureMap::Get()->WaitForTxn(aOwnerId, mForPid, aTxnType, aTxnId); } void RemoteTextureOwnerClient::ClearRecycledTextures() { RemoteTextureMap::Get()->ClearRecycledTextures(mOwnerIds, mForPid, mSharedRecycleBin); } void RemoteTextureOwnerClient::NotifyContextLost( const RemoteTextureOwnerIdSet* aOwnerIds) { if (aOwnerIds) { for (const auto& id : *aOwnerIds) { if (mOwnerIds.find(id) == mOwnerIds.end()) { MOZ_ASSERT_UNREACHABLE("owner id not registered by client"); return; } } } else { aOwnerIds = &mOwnerIds; } if (aOwnerIds->empty()) { return; } RemoteTextureMap::Get()->NotifyContextLost(*aOwnerIds, mForPid); } void RemoteTextureOwnerClient::NotifyContextRestored( const RemoteTextureOwnerIdSet* aOwnerIds) { if (aOwnerIds) { for (const auto& id : *aOwnerIds) { if (mOwnerIds.find(id) == mOwnerIds.end()) { MOZ_ASSERT_UNREACHABLE("owner id not registered by client"); return; } } } else { aOwnerIds = &mOwnerIds; } if (aOwnerIds->empty()) { return; } RemoteTextureMap::Get()->NotifyContextRestored(*aOwnerIds, mForPid); } void RemoteTextureOwnerClient::PushTexture( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, UniquePtr&& aTextureData) { MOZ_ASSERT(IsRegistered(aOwnerId)); RefPtr textureHost = RemoteTextureMap::CreateRemoteTexture( aTextureData.get(), TextureFlags::DEFAULT); if (!textureHost) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } RemoteTextureMap::Get()->PushTexture(aTextureId, aOwnerId, mForPid, std::move(aTextureData), textureHost, /* aResourceWrapper */ nullptr); } void RemoteTextureOwnerClient::PushTexture( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, const std::shared_ptr& aSharedSurface, const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, const SurfaceDescriptor& aDesc) { MOZ_ASSERT(IsRegistered(aOwnerId)); UniquePtr textureData = MakeUnique(aDesc, aFormat, aSize); RefPtr textureHost = RemoteTextureMap::CreateRemoteTexture( textureData.get(), TextureFlags::DEFAULT); if (!textureHost) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } RemoteTextureMap::Get()->PushTexture( aTextureId, aOwnerId, mForPid, std::move(textureData), textureHost, SharedResourceWrapper::SharedSurface(aSharedSurface)); } void RemoteTextureOwnerClient::PushTexture( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, const std::shared_ptr& aExternalTexture, const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, const SurfaceDescriptor& aDesc) { MOZ_ASSERT(IsRegistered(aOwnerId)); UniquePtr textureData = MakeUnique(aDesc, aFormat, aSize); RefPtr textureHost = RemoteTextureMap::CreateRemoteTexture( textureData.get(), TextureFlags::DEFAULT); if (!textureHost) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } RemoteTextureMap::Get()->PushTexture( aTextureId, aOwnerId, mForPid, std::move(textureData), textureHost, SharedResourceWrapper::ExternalTexture(aExternalTexture)); } void RemoteTextureOwnerClient::PushDummyTexture( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId) { MOZ_ASSERT(IsRegistered(aOwnerId)); auto flags = TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE | TextureFlags::DUMMY_TEXTURE; auto* rawData = BufferTextureData::Create( gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR, flags, ALLOC_DEFAULT, nullptr); if (!rawData) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } auto textureData = UniquePtr(rawData); RefPtr textureHost = RemoteTextureMap::CreateRemoteTexture( textureData.get(), TextureFlags::DUMMY_TEXTURE); if (!textureHost) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } RemoteTextureMap::Get()->PushTexture(aTextureId, aOwnerId, mForPid, std::move(textureData), textureHost, /* aResourceWrapper */ nullptr); } void RemoteTextureOwnerClient::GetLatestBufferSnapshot( const RemoteTextureOwnerId aOwnerId, const mozilla::ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) { MOZ_ASSERT(IsRegistered(aOwnerId)); RemoteTextureMap::Get()->GetLatestBufferSnapshot(aOwnerId, mForPid, aDestShmem, aSize); } UniquePtr RemoteTextureOwnerClient::GetRecycledTextureData( const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, TextureType aTextureType, RemoteTextureOwnerId aOwnerId) { return RemoteTextureMap::Get()->GetRecycledTextureData( aOwnerId, mForPid, mSharedRecycleBin, aSize, aFormat, aTextureType); } UniquePtr RemoteTextureOwnerClient::CreateOrRecycleBufferTextureData( const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, RemoteTextureOwnerId aOwnerId) { auto texture = GetRecycledTextureData(aSize, aFormat, TextureType::Unknown, aOwnerId); if (texture) { return texture; } auto flags = TextureFlags::DEALLOCATE_CLIENT | TextureFlags::REMOTE_TEXTURE; auto* data = BufferTextureData::Create(aSize, aFormat, gfx::BackendType::SKIA, LayersBackend::LAYERS_WR, flags, ALLOC_DEFAULT, nullptr); return UniquePtr(data); } std::shared_ptr RemoteTextureOwnerClient::GetRecycledSharedSurface( const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, SurfaceDescriptor::Type aType, RemoteTextureOwnerId aOwnerId) { UniquePtr wrapper = RemoteTextureMap::Get()->RemoteTextureMap::GetRecycledSharedTexture( aOwnerId, mForPid, mSharedRecycleBin, aSize, aFormat, aType); if (!wrapper) { return nullptr; } MOZ_ASSERT(wrapper->mSharedSurface); return wrapper->mSharedSurface; } std::shared_ptr RemoteTextureOwnerClient::GetRecycledExternalTexture( const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, SurfaceDescriptor::Type aType, RemoteTextureOwnerId aOwnerId) { UniquePtr wrapper = RemoteTextureMap::Get()->RemoteTextureMap::GetRecycledSharedTexture( aOwnerId, mForPid, mSharedRecycleBin, aSize, aFormat, aType); if (!wrapper) { return nullptr; } MOZ_ASSERT(wrapper->mExternalTexture); return wrapper->mExternalTexture; } StaticAutoPtr RemoteTextureMap::sInstance; /* static */ void RemoteTextureMap::Init() { MOZ_ASSERT(!sInstance); sInstance = new RemoteTextureMap(); } /* static */ void RemoteTextureMap::Shutdown() { if (sInstance) { sInstance = nullptr; } } RemoteTextureMap::RemoteTextureMap() : mMonitor("RemoteTextureMap::mMonitor") {} RemoteTextureMap::~RemoteTextureMap() = default; bool RemoteTextureMap::RecycleTexture( const RefPtr& aRecycleBin, TextureDataHolder& aHolder, bool aExpireOldTextures) { if (!aHolder.mTextureData || (aHolder.mTextureHost && aHolder.mTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE)) { return false; } // Expire old textures so they don't sit in the list forever if unused. constexpr size_t kSharedTextureLimit = 21; constexpr size_t kTextureLimit = 7; while (aRecycleBin->mRecycledTextures.size() >= (aRecycleBin->mIsShared ? kSharedTextureLimit : kTextureLimit)) { if (!aExpireOldTextures) { // There are too many textures, and we can't expire any to make room. return false; } aRecycleBin->mRecycledTextures.pop_front(); } TextureData::Info info; aHolder.mTextureData->FillInfo(info); RemoteTextureRecycleBin::RecycledTextureHolder recycled{info.size, info.format}; if (aHolder.mResourceWrapper) { // Recycle shared texture SurfaceDescriptor desc; if (!aHolder.mTextureData->Serialize(desc)) { return false; } recycled.mType = desc.type(); recycled.mResourceWrapper = std::move(aHolder.mResourceWrapper); } else { // Recycle texture data recycled.mTextureData = std::move(aHolder.mTextureData); } aRecycleBin->mRecycledTextures.push_back(std::move(recycled)); return true; } void RemoteTextureMap::PushTexture( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, UniquePtr&& aTextureData, RefPtr& aTextureHost, UniquePtr&& aResourceWrapper) { MOZ_RELEASE_ASSERT(aTextureHost); std::vector> releasingTextures; // Release outside the monitor std::vector> renderingReadyCallbacks; // Call outside the monitor { MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, aOwnerId, aForPid); if (!owner) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } if (owner->mIsContextLost && !(aTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE)) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Texture pushed during context lost"; } auto textureData = MakeUnique( aTextureId, aTextureHost, std::move(aTextureData), std::move(aResourceWrapper)); MOZ_ASSERT(owner->mLatestTextureId < aTextureId); owner->mWaitingTextureDataHolders.push_back(std::move(textureData)); { GetRenderingReadyCallbacks(lock, owner, aTextureId, renderingReadyCallbacks); // Update mRemoteTextureHost. // This happens when PushTexture() with RemoteTextureId is called after // GetRemoteTexture() with the RemoteTextureId. const auto key = std::pair(aForPid, aTextureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it != mRemoteTextureHostWrapperHolders.end()) { MOZ_ASSERT(!it->second->mRemoteTextureHost); it->second->mRemoteTextureHost = aTextureHost; } } mMonitor.Notify(); // Release owner->mReleasingRenderedTextureHosts before checking // NumCompositableRefs() if (!owner->mReleasingRenderedTextureHosts.empty()) { std::transform( owner->mReleasingRenderedTextureHosts.begin(), owner->mReleasingRenderedTextureHosts.end(), std::back_inserter(releasingTextures), [](CompositableTextureHostRef& aRef) { return aRef.get(); }); owner->mReleasingRenderedTextureHosts.clear(); } // Drop obsoleted remote textures. while (!owner->mUsingTextureDataHolders.empty()) { auto& front = owner->mUsingTextureDataHolders.front(); // If mLatestRenderedTextureHost is last compositable ref of remote // texture's TextureHost, its RemoteTextureHostWrapper is already // unregistered. It happens when pushed remote textures that follow are // not rendered since last mLatestRenderedTextureHost update. In this // case, remove the TextureHost from mUsingTextureDataHolders. It is for // unblocking remote texture recyclieng. if (front->mTextureHost && front->mTextureHost->NumCompositableRefs() == 1 && front->mTextureHost == owner->mLatestRenderedTextureHost) { owner->mUsingTextureDataHolders.pop_front(); continue; } // When compositable ref of TextureHost becomes 0, the TextureHost is not // used by WebRender anymore. if (front->mTextureHost && front->mTextureHost->NumCompositableRefs() == 0) { owner->mReleasingTextureDataHolders.push_back(std::move(front)); owner->mUsingTextureDataHolders.pop_front(); } else if (front->mTextureHost && front->mTextureHost->NumCompositableRefs() >= 0) { // Remote texture is still in use by WebRender. break; } else { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); owner->mUsingTextureDataHolders.pop_front(); } } while (!owner->mReleasingTextureDataHolders.empty()) { RecycleTexture(owner->mRecycleBin, *owner->mReleasingTextureDataHolders.front(), true); owner->mReleasingTextureDataHolders.pop_front(); } } const auto info = RemoteTextureInfo(aTextureId, aOwnerId, aForPid); for (auto& callback : renderingReadyCallbacks) { callback(info); } } bool RemoteTextureMap::RemoveTexture(const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid) { MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, aOwnerId, aForPid); if (!owner) { return false; } for (auto it = owner->mWaitingTextureDataHolders.begin(); it != owner->mWaitingTextureDataHolders.end(); it++) { auto& data = *it; if (data->mTextureId == aTextureId) { if (mRemoteTextureHostWrapperHolders.find(std::pair( aForPid, aTextureId)) != mRemoteTextureHostWrapperHolders.end()) { return false; } if (!RecycleTexture(owner->mRecycleBin, *data, false)) { owner->mReleasingTextureDataHolders.push_back(std::move(data)); } owner->mWaitingTextureDataHolders.erase(it); return true; } } return false; } void RemoteTextureMap::GetLatestBufferSnapshot( const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, const mozilla::ipc::Shmem& aDestShmem, const gfx::IntSize& aSize) { // The compositable ref of remote texture should be updated in mMonitor lock. CompositableTextureHostRef textureHostRef; RefPtr releasingTexture; // Release outside the monitor std::shared_ptr externalTexture; { MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, aOwnerId, aForPid); if (!owner) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } // Get latest TextureHost of remote Texture. if (owner->mWaitingTextureDataHolders.empty() && owner->mUsingTextureDataHolders.empty()) { return; } const auto* holder = !owner->mWaitingTextureDataHolders.empty() ? owner->mWaitingTextureDataHolders.back().get() : owner->mUsingTextureDataHolders.back().get(); TextureHost* textureHost = holder->mTextureHost; if (textureHost->GetSize() != aSize) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } if (textureHost->GetFormat() != gfx::SurfaceFormat::R8G8B8A8 && textureHost->GetFormat() != gfx::SurfaceFormat::B8G8R8A8) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } if (holder->mResourceWrapper && holder->mResourceWrapper->mExternalTexture) { // Increment compositable ref to prevent that TextureDataHolder is removed // during memcpy. textureHostRef = textureHost; externalTexture = holder->mResourceWrapper->mExternalTexture; } else if (textureHost->AsBufferTextureHost()) { // Increment compositable ref to prevent that TextureDataHolder is removed // during memcpy. textureHostRef = textureHost; } else { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } } if (!textureHostRef) { return; } if (externalTexture) { externalTexture->GetSnapshot(aDestShmem, aSize); } else if (auto* bufferTextureHost = textureHostRef->AsBufferTextureHost()) { uint32_t stride = ImageDataSerializer::ComputeRGBStride( bufferTextureHost->GetFormat(), aSize.width); uint32_t bufferSize = stride * aSize.height; uint8_t* dst = aDestShmem.get(); uint8_t* src = bufferTextureHost->GetBuffer(); MOZ_ASSERT(bufferSize <= aDestShmem.Size()); memcpy(dst, src, bufferSize); } { MonitorAutoLock lock(mMonitor); // Release compositable ref in mMonitor lock, but release RefPtr outside the // monitor releasingTexture = textureHostRef; textureHostRef = nullptr; } } void RemoteTextureMap::RegisterTextureOwner( const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, const RefPtr& aRecycleBin) { MonitorAutoLock lock(mMonitor); const auto key = std::pair(aForPid, aOwnerId); auto it = mTextureOwners.find(key); if (it != mTextureOwners.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } auto owner = MakeUnique(); if (aRecycleBin) { owner->mRecycleBin = aRecycleBin; } else { owner->mRecycleBin = new RemoteTextureRecycleBin(false); } mTextureOwners.emplace(key, std::move(owner)); } void RemoteTextureMap::KeepTextureDataAliveForTextureHostIfNecessary( const MonitorAutoLock& aProofOfLock, RemoteTextureMap::TextureOwner* aOwner, std::deque>& aHolders) { for (auto& holder : aHolders) { // If remote texture of TextureHost still exist, keep // SharedResourceWrapper/TextureData alive while the TextureHost is alive. if (holder->mTextureHost && holder->mTextureHost->NumCompositableRefs() > 0) { RefPtr eventTarget = GetCurrentSerialEventTarget(); RefPtr runnable = NS_NewRunnableFunction( "RemoteTextureMap::UnregisterTextureOwner::Runnable", [data = std::move(holder->mTextureData), wrapper = std::move(holder->mResourceWrapper)]() {}); auto destroyedCallback = [eventTarget = std::move(eventTarget), runnable = std::move(runnable)]() mutable { eventTarget->Dispatch(runnable.forget()); }; holder->mTextureHost->SetDestroyedCallback(destroyedCallback); } else { RecycleTexture(aOwner->mRecycleBin, *holder, true); } } } UniquePtr RemoteTextureMap::UnregisterTextureOwner( MonitorAutoLock& aProofOfLock, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, std::vector>& aReleasingTextures, std::vector>& aRenderingReadyCallbacks) { const auto key = std::pair(aForPid, aOwnerId); auto it = mTextureOwners.find(key); if (it == mTextureOwners.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return nullptr; } auto* owner = it->second.get(); // If waiting for a last use, and it hasn't arrived yet, then defer // unregistering. if (owner->mWaitForTxn) { owner->mDeferUnregister = GetCurrentSerialEventTarget(); // If another thread is waiting on this owner to produce textures, // it must be notified that owner is going away. if (!owner->mLatestTextureHost && owner->mWaitingTextureDataHolders.empty()) { aProofOfLock.Notify(); } return nullptr; } if (owner->mLatestTextureHost) { // Release CompositableRef in mMonitor aReleasingTextures.emplace_back(owner->mLatestTextureHost); owner->mLatestTextureHost = nullptr; } // mReleasingRenderedTextureHosts and mLatestRenderedTextureHost could // simply be cleared. Since NumCompositableRefs() > 0 keeps TextureHosts in // mUsingTextureDataHolders alive. They need to be cleared before // KeepTextureDataAliveForTextureHostIfNecessary() call. The function uses // NumCompositableRefs(). if (!owner->mReleasingRenderedTextureHosts.empty()) { std::transform(owner->mReleasingRenderedTextureHosts.begin(), owner->mReleasingRenderedTextureHosts.end(), std::back_inserter(aReleasingTextures), [](CompositableTextureHostRef& aRef) { return aRef.get(); }); owner->mReleasingRenderedTextureHosts.clear(); } if (owner->mLatestRenderedTextureHost) { owner->mLatestRenderedTextureHost = nullptr; } GetAllRenderingReadyCallbacks(aProofOfLock, owner, aRenderingReadyCallbacks); KeepTextureDataAliveForTextureHostIfNecessary( aProofOfLock, owner, owner->mWaitingTextureDataHolders); KeepTextureDataAliveForTextureHostIfNecessary( aProofOfLock, owner, owner->mUsingTextureDataHolders); KeepTextureDataAliveForTextureHostIfNecessary( aProofOfLock, owner, owner->mReleasingTextureDataHolders); UniquePtr releasingOwner = std::move(it->second); mTextureOwners.erase(it); return releasingOwner; } void RemoteTextureMap::UnregisterTextureOwner( const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid) { UniquePtr releasingOwner; // Release outside the monitor std::vector> releasingTextures; // Release outside the monitor std::vector> renderingReadyCallbacks; // Call outside the monitor { MonitorAutoLock lock(mMonitor); releasingOwner = UnregisterTextureOwner( lock, aOwnerId, aForPid, releasingTextures, renderingReadyCallbacks); if (!releasingOwner) { return; } mMonitor.Notify(); } const auto info = RemoteTextureInfo(RemoteTextureId{0}, RemoteTextureOwnerId{0}, 0); for (auto& callback : renderingReadyCallbacks) { callback(info); } } void RemoteTextureMap::UnregisterTextureOwners( const RemoteTextureOwnerIdSet& aOwnerIds, const base::ProcessId aForPid) { std::vector> releasingOwners; // Release outside the monitor std::vector> releasingTextures; // Release outside the monitor std::vector> renderingReadyCallbacks; // Call outside the monitor { MonitorAutoLock lock(mMonitor); for (const auto& id : aOwnerIds) { if (auto releasingOwner = UnregisterTextureOwner( lock, id, aForPid, releasingTextures, renderingReadyCallbacks)) { releasingOwners.push_back(std::move(releasingOwner)); } } if (releasingOwners.empty()) { return; } mMonitor.Notify(); } const auto info = RemoteTextureInfo(RemoteTextureId{0}, RemoteTextureOwnerId{0}, 0); for (auto& callback : renderingReadyCallbacks) { callback(info); } } already_AddRefed RemoteTextureMap::RegisterTxnScheduler(base::ProcessId aForPid, RemoteTextureTxnType aType) { MonitorAutoLock lock(mMonitor); const auto key = std::pair(aForPid, aType); auto it = mTxnSchedulers.find(key); if (it != mTxnSchedulers.end()) { return do_AddRef(it->second); } RefPtr scheduler( new RemoteTextureTxnScheduler(aForPid, aType)); mTxnSchedulers.emplace(key, scheduler.get()); return scheduler.forget(); } void RemoteTextureMap::UnregisterTxnScheduler(base::ProcessId aForPid, RemoteTextureTxnType aType) { MonitorAutoLock lock(mMonitor); const auto key = std::pair(aForPid, aType); auto it = mTxnSchedulers.find(key); if (it == mTxnSchedulers.end()) { MOZ_ASSERT_UNREACHABLE("Remote texture txn scheduler does not exist."); return; } mTxnSchedulers.erase(it); } already_AddRefed RemoteTextureTxnScheduler::Create( mozilla::ipc::IProtocol* aProtocol) { if (auto* instance = RemoteTextureMap::Get()) { if (auto* toplevel = aProtocol->ToplevelProtocol()) { auto pid = toplevel->OtherPidMaybeInvalid(); if (pid != base::kInvalidProcessId) { return instance->RegisterTxnScheduler(pid, toplevel->GetProtocolId()); } } } return nullptr; } RemoteTextureTxnScheduler::~RemoteTextureTxnScheduler() { NotifyTxn(std::numeric_limits::max()); RemoteTextureMap::Get()->UnregisterTxnScheduler(mForPid, mType); } void RemoteTextureTxnScheduler::NotifyTxn(RemoteTextureTxnId aTxnId) { MonitorAutoLock lock(RemoteTextureMap::Get()->mMonitor); mLastTxnId = aTxnId; for (; !mWaits.empty(); mWaits.pop_front()) { auto& wait = mWaits.front(); if (wait.mTxnId > aTxnId) { break; } RemoteTextureMap::Get()->NotifyTxn(lock, wait.mOwnerId, mForPid); } } bool RemoteTextureTxnScheduler::WaitForTxn(const MonitorAutoLock& aProofOfLock, RemoteTextureOwnerId aOwnerId, RemoteTextureTxnId aTxnId) { if (aTxnId <= mLastTxnId) { return false; } mWaits.insert(std::upper_bound(mWaits.begin(), mWaits.end(), aTxnId), Wait{aOwnerId, aTxnId}); return true; } void RemoteTextureMap::ClearRecycledTextures( const RemoteTextureOwnerIdSet& aOwnerIds, const base::ProcessId aForPid, const RefPtr& aRecycleBin) { std::list releasingTextures; // Release outside the monitor { MonitorAutoLock lock(mMonitor); if (aRecycleBin) { releasingTextures.splice(releasingTextures.end(), aRecycleBin->mRecycledTextures); } for (const auto& id : aOwnerIds) { const auto key = std::pair(aForPid, id); auto it = mTextureOwners.find(key); if (it == mTextureOwners.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); continue; } auto& owner = it->second; releasingTextures.splice(releasingTextures.end(), owner->mRecycleBin->mRecycledTextures); } } } void RemoteTextureMap::NotifyContextLost( const RemoteTextureOwnerIdSet& aOwnerIds, const base::ProcessId aForPid) { MonitorAutoLock lock(mMonitor); bool changed = false; for (const auto& id : aOwnerIds) { const auto key = std::pair(aForPid, id); auto it = mTextureOwners.find(key); if (it == mTextureOwners.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); continue; } auto& owner = it->second; if (!owner->mIsContextLost) { owner->mIsContextLost = true; changed = true; } } if (changed) { mMonitor.Notify(); } } void RemoteTextureMap::NotifyContextRestored( const RemoteTextureOwnerIdSet& aOwnerIds, const base::ProcessId aForPid) { MonitorAutoLock lock(mMonitor); bool changed = false; for (const auto& id : aOwnerIds) { const auto key = std::pair(aForPid, id); auto it = mTextureOwners.find(key); if (it == mTextureOwners.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); continue; } auto& owner = it->second; if (owner->mIsContextLost) { owner->mIsContextLost = false; changed = true; } } if (changed) { mMonitor.Notify(); } } /* static */ RefPtr RemoteTextureMap::CreateRemoteTexture( TextureData* aTextureData, TextureFlags aTextureFlags) { SurfaceDescriptor desc; DebugOnly ret = aTextureData->Serialize(desc); MOZ_ASSERT(ret); TextureFlags flags = aTextureFlags | TextureFlags::REMOTE_TEXTURE | TextureFlags::DEALLOCATE_CLIENT; Maybe externalImageId = Nothing(); RefPtr textureHost = TextureHost::Create(desc, null_t(), nullptr, LayersBackend::LAYERS_WR, flags, externalImageId); MOZ_ASSERT(textureHost); if (!textureHost) { gfxCriticalNoteOnce << "Failed to create remote texture"; return nullptr; } textureHost->EnsureRenderTexture(Nothing()); return textureHost; } void RemoteTextureMap::UpdateTexture(const MonitorAutoLock& aProofOfLock, RemoteTextureMap::TextureOwner* aOwner, const RemoteTextureId aTextureId) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(aOwner); MOZ_ASSERT(aTextureId >= aOwner->mLatestTextureId); if (aTextureId == aOwner->mLatestTextureId) { // No need to update texture. return; } // Move remote textures to mUsingTextureDataHolders. while (!aOwner->mWaitingTextureDataHolders.empty()) { auto& front = aOwner->mWaitingTextureDataHolders.front(); if (aTextureId < front->mTextureId) { break; } MOZ_RELEASE_ASSERT(front->mTextureHost); aOwner->mLatestTextureHost = front->mTextureHost; aOwner->mLatestTextureId = front->mTextureId; UniquePtr holder = std::move(front); aOwner->mWaitingTextureDataHolders.pop_front(); // If there are textures not being used by the compositor that will be // obsoleted by this new texture, then queue them for removal later on // the creating thread. while (!aOwner->mUsingTextureDataHolders.empty()) { auto& back = aOwner->mUsingTextureDataHolders.back(); if (back->mTextureHost && back->mTextureHost->NumCompositableRefs() == 0) { if (!RecycleTexture(aOwner->mRecycleBin, *back, false)) { aOwner->mReleasingTextureDataHolders.push_back(std::move(back)); } aOwner->mUsingTextureDataHolders.pop_back(); continue; } break; } aOwner->mUsingTextureDataHolders.push_back(std::move(holder)); } } void RemoteTextureMap::GetRenderingReadyCallbacks( const MonitorAutoLock& aProofOfLock, RemoteTextureMap::TextureOwner* aOwner, const RemoteTextureId aTextureId, std::vector>& aFunctions) { MOZ_ASSERT(aOwner); while (!aOwner->mRenderingReadyCallbackHolders.empty()) { auto& front = aOwner->mRenderingReadyCallbackHolders.front(); if (aTextureId < front->mTextureId) { break; } if (front->mCallback) { aFunctions.push_back(std::move(front->mCallback)); } aOwner->mRenderingReadyCallbackHolders.pop_front(); } } void RemoteTextureMap::GetAllRenderingReadyCallbacks( const MonitorAutoLock& aProofOfLock, RemoteTextureMap::TextureOwner* aOwner, std::vector>& aFunctions) { GetRenderingReadyCallbacks(aProofOfLock, aOwner, RemoteTextureId::Max(), aFunctions); MOZ_ASSERT(aOwner->mRenderingReadyCallbackHolders.empty()); } bool RemoteTextureMap::GetRemoteTexture( RemoteTextureHostWrapper* aTextureHostWrapper, std::function&& aReadyCallback, bool aWaitForRemoteTextureOwner) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(aTextureHostWrapper); if (aTextureHostWrapper->IsReadyForRendering()) { return false; } const auto& textureId = aTextureHostWrapper->mTextureId; const auto& ownerId = aTextureHostWrapper->mOwnerId; const auto& forPid = aTextureHostWrapper->mForPid; const auto& size = aTextureHostWrapper->mSize; RefPtr textureHost; { MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, ownerId, forPid); // If there is no texture owner yet, then we might need to wait for one to // be created, if allowed. If so, we must also wait for an initial texture // host to be created so we can use it. if (!owner || (aWaitForRemoteTextureOwner && !owner->mLatestTextureHost && owner->mWaitingTextureDataHolders.empty())) { if (!aWaitForRemoteTextureOwner) { return false; } const TimeDuration timeout = TimeDuration::FromMilliseconds(10000); while (!owner || (!owner->mLatestTextureHost && owner->mWaitingTextureDataHolders.empty())) { if (owner && (owner->mIsContextLost || owner->mDeferUnregister)) { // If the context was lost, no further updates are expected. return false; } CVStatus status = mMonitor.Wait(timeout); if (status == CVStatus::Timeout) { return false; } owner = GetTextureOwner(lock, ownerId, forPid); } } UpdateTexture(lock, owner, textureId); if (owner->mLatestTextureHost && (owner->mLatestTextureHost->GetFlags() & TextureFlags::DUMMY_TEXTURE)) { // Remote texture allocation was failed. return false; } if (textureId == owner->mLatestTextureId) { MOZ_ASSERT(owner->mLatestTextureHost); MOZ_ASSERT(owner->mLatestTextureHost->GetSize() == size); if (owner->mLatestTextureHost->GetSize() != size) { gfxCriticalNoteOnce << "unexpected remote texture size: " << owner->mLatestTextureHost->GetSize() << " expected: " << size; } textureHost = owner->mLatestTextureHost; } else { if (aReadyCallback) { auto callbackHolder = MakeUnique( textureId, std::move(aReadyCallback)); owner->mRenderingReadyCallbackHolders.push_back( std::move(callbackHolder)); return true; } } // Update mRemoteTextureHost if (textureId == owner->mLatestTextureId) { const auto key = std::pair(forPid, textureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it != mRemoteTextureHostWrapperHolders.end() && !it->second->mRemoteTextureHost) { it->second->mRemoteTextureHost = owner->mLatestTextureHost; } else { MOZ_ASSERT(it->second->mRemoteTextureHost == owner->mLatestTextureHost); } } if (textureHost) { aTextureHostWrapper->SetRemoteTextureHost(lock, textureHost); aTextureHostWrapper->ApplyTextureFlagsToRemoteTexture(); } } return false; } void RemoteTextureMap::NotifyTxn(const MonitorAutoLock& aProofOfLock, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid) { if (auto* owner = GetTextureOwner(aProofOfLock, aOwnerId, aForPid)) { if (!owner->mWaitForTxn) { MOZ_ASSERT_UNREACHABLE("Expected texture owner to wait for txn."); return; } owner->mWaitForTxn = false; if (!owner->mDeferUnregister) { // If unregistering was not deferred, then don't try to force // unregistering yet. return; } owner->mDeferUnregister->Dispatch(NS_NewRunnableFunction( "RemoteTextureMap::SetLastRemoteTextureUse::Runnable", [aOwnerId, aForPid]() { RemoteTextureMap::Get()->UnregisterTextureOwner(aOwnerId, aForPid); })); } } bool RemoteTextureMap::WaitForTxn(const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, RemoteTextureTxnType aTxnType, RemoteTextureTxnId aTxnId) { MonitorAutoLock lock(mMonitor); if (auto* owner = GetTextureOwner(lock, aOwnerId, aForPid)) { if (owner->mDeferUnregister) { MOZ_ASSERT_UNREACHABLE( "Texture owner must wait for txn before unregistering."); return false; } if (owner->mWaitForTxn) { MOZ_ASSERT_UNREACHABLE("Texture owner already waiting for txn."); return false; } const auto key = std::pair(aForPid, aTxnType); auto it = mTxnSchedulers.find(key); if (it == mTxnSchedulers.end()) { // During shutdown, different toplevel protocols may go away in // disadvantageous orders, causing us to sometimes be processing // waits even though the source of transactions upon which the // wait depends shut down. This is generally harmless to ignore, // as it means no further transactions will be generated of that // type and all such transactions have been processed before it // unregistered. NS_WARNING("Could not find scheduler for txn type."); return false; } if (it->second->WaitForTxn(lock, aOwnerId, aTxnId)) { owner->mWaitForTxn = true; } return true; } return false; } void RemoteTextureMap::ReleaseRemoteTextureHost( RemoteTextureHostWrapper* aTextureHostWrapper) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MOZ_ASSERT(aTextureHostWrapper); RefPtr releasingTexture; // Release outside the mutex { MonitorAutoLock lock(mMonitor); releasingTexture = aTextureHostWrapper->GetRemoteTextureHost(lock); aTextureHostWrapper->ClearRemoteTextureHost(lock); } } RefPtr RemoteTextureMap::GetOrCreateRemoteTextureHostWrapper( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, const gfx::IntSize& aSize, const TextureFlags aFlags) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MonitorAutoLock lock(mMonitor); const auto key = std::pair(aForPid, aTextureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it != mRemoteTextureHostWrapperHolders.end()) { return it->second->mRemoteTextureHostWrapper; } auto wrapper = RemoteTextureHostWrapper::Create(aTextureId, aOwnerId, aForPid, aSize, aFlags); auto wrapperHolder = MakeUnique(wrapper); mRemoteTextureHostWrapperHolders.emplace(key, std::move(wrapperHolder)); return wrapper; } void RemoteTextureMap::UnregisterRemoteTextureHostWrapper( const RemoteTextureId aTextureId, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); std::vector> releasingTextures; // Release outside the monitor { MonitorAutoLock lock(mMonitor); const auto key = std::pair(aForPid, aTextureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it == mRemoteTextureHostWrapperHolders.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } releasingTextures.emplace_back(it->second->mRemoteTextureHostWrapper); if (it->second->mRemoteTextureHost) { releasingTextures.emplace_back(it->second->mRemoteTextureHost); } mRemoteTextureHostWrapperHolders.erase(it); mMonitor.Notify(); } } bool RemoteTextureMap::CheckRemoteTextureReady( const RemoteTextureInfo& aInfo, std::function&& aCallback) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, aInfo.mOwnerId, aInfo.mForPid); if (!owner || owner->mIsContextLost) { // Owner is already removed or context lost. No need to wait texture ready. return true; } const auto key = std::pair(aInfo.mForPid, aInfo.mTextureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it == mRemoteTextureHostWrapperHolders.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Remote texture does not exist id:" << uint64_t(aInfo.mTextureId); return true; } if (it->second->mRemoteTextureHost) { return true; } MOZ_ASSERT(!it->second->mRemoteTextureHost); // Check if RemoteTextureId is as expected. if (!owner->mRenderingReadyCallbackHolders.empty()) { auto& front = owner->mRenderingReadyCallbackHolders.front(); MOZ_RELEASE_ASSERT(aInfo.mTextureId >= front->mTextureId); } auto callbackHolder = MakeUnique( aInfo.mTextureId, std::move(aCallback)); owner->mRenderingReadyCallbackHolders.push_back(std::move(callbackHolder)); return false; } bool RemoteTextureMap::WaitRemoteTextureReady(const RemoteTextureInfo& aInfo) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MonitorAutoLock lock(mMonitor); auto* owner = GetTextureOwner(lock, aInfo.mOwnerId, aInfo.mForPid); if (!owner || owner->mIsContextLost) { // Owner is already removed or context lost. return false; } const auto key = std::pair(aInfo.mForPid, aInfo.mTextureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it == mRemoteTextureHostWrapperHolders.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Remote texture does not exist id:" << uint64_t(aInfo.mTextureId); return false; } const TimeDuration timeout = TimeDuration::FromMilliseconds(1000); TextureHost* remoteTexture = it->second->mRemoteTextureHost; while (!remoteTexture) { CVStatus status = mMonitor.Wait(timeout); if (status == CVStatus::Timeout) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); gfxCriticalNoteOnce << "Remote texture wait time out id:" << uint64_t(aInfo.mTextureId); return false; } auto it = mRemoteTextureHostWrapperHolders.find(key); if (it == mRemoteTextureHostWrapperHolders.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return false; } remoteTexture = it->second->mRemoteTextureHost; if (!remoteTexture) { auto* owner = GetTextureOwner(lock, aInfo.mOwnerId, aInfo.mForPid); // When owner is alreay unregistered, remote texture will not be pushed. if (!owner || owner->mIsContextLost) { // This could happen with IPC abnormal shutdown return false; } } } return true; } void RemoteTextureMap::SuppressRemoteTextureReadyCheck( const RemoteTextureId aTextureId, const base::ProcessId aForPid) { MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread()); MonitorAutoLock lock(mMonitor); const auto key = std::pair(aForPid, aTextureId); auto it = mRemoteTextureHostWrapperHolders.find(key); if (it == mRemoteTextureHostWrapperHolders.end()) { MOZ_ASSERT_UNREACHABLE("unexpected to be called"); return; } it->second->mReadyCheckSuppressed = true; } UniquePtr RemoteTextureMap::GetRecycledTextureData( const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, const RefPtr& aRecycleBin, const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat, TextureType aTextureType) { MonitorAutoLock lock(mMonitor); RefPtr bin; if (aOwnerId.IsValid()) { if (auto* owner = GetTextureOwner(lock, aOwnerId, aForPid)) { bin = owner->mRecycleBin; } } else { bin = aRecycleBin; } if (!bin) { return nullptr; } for (auto it = bin->mRecycledTextures.begin(); it != bin->mRecycledTextures.end(); it++) { auto& holder = *it; if (holder.mTextureData && holder.mTextureData->GetTextureType() == aTextureType && holder.mSize == aSize && holder.mFormat == aFormat) { UniquePtr texture = std::move(holder.mTextureData); bin->mRecycledTextures.erase(it); return texture; } } return nullptr; } UniquePtr RemoteTextureMap::GetRecycledSharedTexture( const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid, const RefPtr& aRecycleBin, const gfx::IntSize& aSize, const gfx::SurfaceFormat aFormat, SurfaceDescriptor::Type aType) { MonitorAutoLock lock(mMonitor); RefPtr bin; if (aOwnerId.IsValid()) { if (auto* owner = GetTextureOwner(lock, aOwnerId, aForPid)) { bin = owner->mRecycleBin; } } else { bin = aRecycleBin; } if (!bin) { return nullptr; } for (auto it = bin->mRecycledTextures.begin(); it != bin->mRecycledTextures.end(); it++) { auto& holder = *it; if (holder.mType == aType && holder.mSize == aSize && holder.mFormat == aFormat) { UniquePtr wrapper = std::move(holder.mResourceWrapper); bin->mRecycledTextures.erase(it); return wrapper; } } return nullptr; } RemoteTextureMap::TextureOwner* RemoteTextureMap::GetTextureOwner( const MonitorAutoLock& aProofOfLock, const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid) { const auto key = std::pair(aForPid, aOwnerId); auto it = mTextureOwners.find(key); if (it == mTextureOwners.end()) { return nullptr; } return it->second.get(); } RemoteTextureMap::TextureDataHolder::TextureDataHolder( const RemoteTextureId aTextureId, RefPtr aTextureHost, UniquePtr&& aTextureData, UniquePtr&& aResourceWrapper) : mTextureId(aTextureId), mTextureHost(aTextureHost), mTextureData(std::move(aTextureData)), mResourceWrapper(std::move(aResourceWrapper)) {} RemoteTextureMap::RenderingReadyCallbackHolder::RenderingReadyCallbackHolder( const RemoteTextureId aTextureId, std::function&& aCallback) : mTextureId(aTextureId), mCallback(aCallback) {} RemoteTextureMap::RemoteTextureHostWrapperHolder:: RemoteTextureHostWrapperHolder( RefPtr aRemoteTextureHostWrapper) : mRemoteTextureHostWrapper(aRemoteTextureHostWrapper) {} } // namespace mozilla::layers