forked from mirrors/gecko-dev
WebGPU uses CompositableInProcessManager to push TextureHost directly from WebGPUParent to WebRender. But CompositableInProcessManager plumbing has a problem and caused Bug 1805209. gecko already has a similar mechanism, called RemoteTextureMap. It is used in oop WebGL. If WebGPU uses RemoteTextureMap instead of CompositableInProcessManager, both WebGPU and oop WebGL use same mechanism. WebGPUParent pushes a new texture to RemoteTextureMap. The RemoteTextureMap notifies the pushed texture to WebRenderImageHost. Before the change, only one TextureHost is used for one swap chain. With the change, multiple TextureHosts are used for one swap chain with recycling. The changes are followings. - Use RemoteTextureMap instead of CompositableInProcessManager. - Use RemoteTextureOwnerId instead of CompositableHandle. - Use WebRenderCanvasData instead of WebRenderInProcessImageData. - Add remote texture pushed callback functionality to RemoteTextureMap. With it, RemoteTextureMap notifies a new pushed remote texture to WebRenderImageHost. - Remove CompositableInProcessManager. Differential Revision: https://phabricator.services.mozilla.com/D164890
300 lines
9.9 KiB
C++
300 lines
9.9 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 "WebRenderImageHost.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "mozilla/ScopeExit.h"
|
|
#include "mozilla/layers/AsyncImagePipelineManager.h"
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/layers/CompositorVsyncScheduler.h" // for CompositorVsyncScheduler
|
|
#include "mozilla/layers/RemoteTextureHostWrapper.h"
|
|
#include "mozilla/layers/RemoteTextureMap.h"
|
|
#include "mozilla/layers/WebRenderBridgeParent.h"
|
|
#include "mozilla/layers/WebRenderTextureHost.h"
|
|
#include "nsAString.h"
|
|
#include "nsDebug.h" // for NS_WARNING, NS_ASSERTION
|
|
#include "nsPrintfCString.h" // for nsPrintfCString
|
|
#include "nsString.h" // for nsAutoCString
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace gfx;
|
|
|
|
namespace layers {
|
|
|
|
class ISurfaceAllocator;
|
|
|
|
WebRenderImageHost::WebRenderImageHost(const TextureInfo& aTextureInfo)
|
|
: CompositableHost(aTextureInfo),
|
|
ImageComposite(),
|
|
mCurrentAsyncImageManager(nullptr) {}
|
|
|
|
WebRenderImageHost::~WebRenderImageHost() {
|
|
MOZ_ASSERT(!mRemoteTextureHost);
|
|
MOZ_ASSERT(mWrBridges.empty());
|
|
}
|
|
|
|
void WebRenderImageHost::OnReleased() {
|
|
if (mRemoteTextureOwnerIdOfPushCallback) {
|
|
RemoteTextureMap::Get()->UnregisterRemoteTexturePushListener(
|
|
*mRemoteTextureOwnerIdOfPushCallback, mForPidOfPushCallback, this);
|
|
mRemoteTextureOwnerIdOfPushCallback = Nothing();
|
|
mSizeOfPushCallback = gfx::IntSize();
|
|
mFlagsOfPushCallback = TextureFlags::NO_FLAGS;
|
|
}
|
|
|
|
if (mRemoteTextureHost) {
|
|
mRemoteTextureHost = nullptr;
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::UseTextureHost(
|
|
const nsTArray<TimedTexture>& aTextures) {
|
|
CompositableHost::UseTextureHost(aTextures);
|
|
MOZ_ASSERT(aTextures.Length() >= 1);
|
|
|
|
if (mRemoteTextureHost) {
|
|
mRemoteTextureHost = nullptr;
|
|
}
|
|
|
|
nsTArray<TimedImage> newImages;
|
|
|
|
for (uint32_t i = 0; i < aTextures.Length(); ++i) {
|
|
const TimedTexture& t = aTextures[i];
|
|
MOZ_ASSERT(t.mTexture);
|
|
if (i + 1 < aTextures.Length() && t.mProducerID == mLastProducerID &&
|
|
t.mFrameID < mLastFrameID) {
|
|
// Ignore frames before a frame that we already composited. We don't
|
|
// ever want to display these frames. This could be important if
|
|
// the frame producer adjusts timestamps (e.g. to track the audio clock)
|
|
// and the new frame times are earlier.
|
|
continue;
|
|
}
|
|
TimedImage& img = *newImages.AppendElement();
|
|
img.mTextureHost = t.mTexture;
|
|
img.mTimeStamp = t.mTimeStamp;
|
|
img.mPictureRect = t.mPictureRect;
|
|
img.mFrameID = t.mFrameID;
|
|
img.mProducerID = t.mProducerID;
|
|
img.mTextureHost->SetCropRect(img.mPictureRect);
|
|
}
|
|
|
|
SetImages(std::move(newImages));
|
|
|
|
if (GetAsyncRef()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge && wrBridge->CompositorScheduler()) {
|
|
wrBridge->CompositorScheduler()->ScheduleComposition(
|
|
wr::RenderReasons::ASYNC_IMAGE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Video producers generally send replacement images with the same frameID but
|
|
// slightly different timestamps in order to sync with the audio clock. This
|
|
// means that any CompositeUntil() call we made in Composite() may no longer
|
|
// guarantee that we'll composite until the next frame is ready. Fix that
|
|
// here.
|
|
if (mLastFrameID >= 0 && !mWrBridges.empty()) {
|
|
for (const auto& img : Images()) {
|
|
bool frameComesAfter =
|
|
img.mFrameID > mLastFrameID || img.mProducerID != mLastProducerID;
|
|
if (frameComesAfter && !img.mTimeStamp.IsNull()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge) {
|
|
wrBridge->AsyncImageManager()->CompositeUntil(
|
|
img.mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::UseRemoteTexture(const RemoteTextureId aTextureId,
|
|
const RemoteTextureOwnerId aOwnerId,
|
|
const base::ProcessId aForPid,
|
|
const gfx::IntSize aSize,
|
|
const TextureFlags aFlags) {
|
|
RefPtr<TextureHost> texture =
|
|
RemoteTextureMap::Get()->GetOrCreateRemoteTextureHostWrapper(
|
|
aTextureId, aOwnerId, aForPid, aSize, aFlags);
|
|
mRemoteTextureHost = texture;
|
|
if (mRemoteTextureHost) {
|
|
mRemoteTextureHost->AsRemoteTextureHostWrapper()
|
|
->CheckIsReadyForRendering();
|
|
}
|
|
|
|
SetCurrentTextureHost(mRemoteTextureHost);
|
|
|
|
if (GetAsyncRef()) {
|
|
for (const auto& it : mWrBridges) {
|
|
RefPtr<WebRenderBridgeParent> wrBridge = it.second->WrBridge();
|
|
if (wrBridge && wrBridge->CompositorScheduler()) {
|
|
wrBridge->CompositorScheduler()->ScheduleComposition(
|
|
wr::RenderReasons::ASYNC_IMAGE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::EnableRemoteTexturePushCallback(
|
|
const RemoteTextureOwnerId aOwnerId, const base::ProcessId aForPid,
|
|
const gfx::IntSize aSize, const TextureFlags aFlags) {
|
|
if (!GetAsyncRef()) {
|
|
MOZ_ASSERT_UNREACHABLE("unexpected to be called");
|
|
return;
|
|
}
|
|
|
|
if (mRemoteTextureOwnerIdOfPushCallback.isSome()) {
|
|
RemoteTextureMap::Get()->UnregisterRemoteTexturePushListener(aOwnerId,
|
|
aForPid, this);
|
|
}
|
|
|
|
RemoteTextureMap::Get()->RegisterRemoteTexturePushListener(aOwnerId, aForPid,
|
|
this);
|
|
mRemoteTextureOwnerIdOfPushCallback = Some(aOwnerId);
|
|
mForPidOfPushCallback = aForPid;
|
|
mSizeOfPushCallback = aSize;
|
|
mFlagsOfPushCallback = aFlags;
|
|
}
|
|
|
|
void WebRenderImageHost::NotifyPushTexture(const RemoteTextureId aTextureId,
|
|
const RemoteTextureOwnerId aOwnerId,
|
|
const base::ProcessId aForPid) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
|
|
if (mRemoteTextureOwnerIdOfPushCallback != Some(aOwnerId)) {
|
|
// RemoteTextureOwnerId is already obsoleted
|
|
return;
|
|
}
|
|
UseRemoteTexture(aTextureId, aOwnerId, aForPid, mSizeOfPushCallback,
|
|
mFlagsOfPushCallback);
|
|
}
|
|
|
|
void WebRenderImageHost::CleanupResources() {
|
|
ClearImages();
|
|
SetCurrentTextureHost(nullptr);
|
|
}
|
|
|
|
void WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture) {
|
|
CompositableHost::RemoveTextureHost(aTexture);
|
|
RemoveImagesWithTextureHost(aTexture);
|
|
}
|
|
|
|
TimeStamp WebRenderImageHost::GetCompositionTime() const {
|
|
TimeStamp time;
|
|
|
|
MOZ_ASSERT(mCurrentAsyncImageManager);
|
|
if (mCurrentAsyncImageManager) {
|
|
time = mCurrentAsyncImageManager->GetCompositionTime();
|
|
}
|
|
return time;
|
|
}
|
|
|
|
CompositionOpportunityId WebRenderImageHost::GetCompositionOpportunityId()
|
|
const {
|
|
CompositionOpportunityId id;
|
|
|
|
MOZ_ASSERT(mCurrentAsyncImageManager);
|
|
if (mCurrentAsyncImageManager) {
|
|
id = mCurrentAsyncImageManager->GetCompositionOpportunityId();
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void WebRenderImageHost::AppendImageCompositeNotification(
|
|
const ImageCompositeNotificationInfo& aInfo) const {
|
|
if (mCurrentAsyncImageManager) {
|
|
mCurrentAsyncImageManager->AppendImageCompositeNotification(aInfo);
|
|
}
|
|
}
|
|
|
|
TextureHost* WebRenderImageHost::GetAsTextureHostForComposite(
|
|
AsyncImagePipelineManager* aAsyncImageManager) {
|
|
if (mRemoteTextureHost) {
|
|
return mCurrentTextureHost;
|
|
}
|
|
|
|
mCurrentAsyncImageManager = aAsyncImageManager;
|
|
const auto onExit =
|
|
mozilla::MakeScopeExit([&]() { mCurrentAsyncImageManager = nullptr; });
|
|
|
|
int imageIndex = ChooseImageIndex();
|
|
if (imageIndex < 0) {
|
|
SetCurrentTextureHost(nullptr);
|
|
return nullptr;
|
|
}
|
|
|
|
if (uint32_t(imageIndex) + 1 < ImagesCount()) {
|
|
mCurrentAsyncImageManager->CompositeUntil(
|
|
GetImage(imageIndex + 1)->mTimeStamp +
|
|
TimeDuration::FromMilliseconds(BIAS_TIME_MS));
|
|
}
|
|
|
|
const TimedImage* img = GetImage(imageIndex);
|
|
SetCurrentTextureHost(img->mTextureHost);
|
|
|
|
if (mCurrentAsyncImageManager->GetCompositionTime()) {
|
|
// We are in a composition. Send ImageCompositeNotifications.
|
|
OnFinishRendering(imageIndex, img, mAsyncRef.mProcessId, mAsyncRef.mHandle);
|
|
}
|
|
|
|
return mCurrentTextureHost;
|
|
}
|
|
|
|
void WebRenderImageHost::SetCurrentTextureHost(TextureHost* aTexture) {
|
|
if (aTexture == mCurrentTextureHost.get()) {
|
|
return;
|
|
}
|
|
mCurrentTextureHost = aTexture;
|
|
}
|
|
|
|
void WebRenderImageHost::Dump(std::stringstream& aStream, const char* aPrefix,
|
|
bool aDumpHtml) {
|
|
for (const auto& img : Images()) {
|
|
aStream << aPrefix;
|
|
aStream << (aDumpHtml ? "<ul><li>TextureHost: " : "TextureHost: ");
|
|
DumpTextureHost(aStream, img.mTextureHost);
|
|
aStream << (aDumpHtml ? " </li></ul> " : " ");
|
|
}
|
|
}
|
|
|
|
void WebRenderImageHost::SetWrBridge(const wr::PipelineId& aPipelineId,
|
|
WebRenderBridgeParent* aWrBridge) {
|
|
MOZ_ASSERT(aWrBridge);
|
|
MOZ_ASSERT(!mCurrentAsyncImageManager);
|
|
#ifdef DEBUG
|
|
const auto it = mWrBridges.find(wr::AsUint64(aPipelineId));
|
|
MOZ_ASSERT(it == mWrBridges.end());
|
|
#endif
|
|
RefPtr<WebRenderBridgeParentRef> ref =
|
|
aWrBridge->GetWebRenderBridgeParentRef();
|
|
mWrBridges.emplace(wr::AsUint64(aPipelineId), ref);
|
|
}
|
|
|
|
void WebRenderImageHost::ClearWrBridge(const wr::PipelineId& aPipelineId,
|
|
WebRenderBridgeParent* aWrBridge) {
|
|
MOZ_ASSERT(aWrBridge);
|
|
MOZ_ASSERT(!mCurrentAsyncImageManager);
|
|
|
|
const auto it = mWrBridges.find(wr::AsUint64(aPipelineId));
|
|
MOZ_ASSERT(it != mWrBridges.end());
|
|
if (it == mWrBridges.end()) {
|
|
gfxCriticalNote << "WrBridge mismatch happened";
|
|
return;
|
|
}
|
|
mWrBridges.erase(it);
|
|
SetCurrentTextureHost(nullptr);
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|