fune/gfx/layers/wr/WebRenderUserData.cpp
sotaro 954c16acec Bug 1805209 - Use RemoteTexture for WebGPU r=gfx-reviewers,lsalzman
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
2022-12-23 20:41:02 +00:00

427 lines
14 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 "WebRenderUserData.h"
#include "mozilla/image/WebRenderImageProvider.h"
#include "mozilla/layers/AnimationHelper.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/ImageClient.h"
#include "mozilla/layers/WebRenderBridgeChild.h"
#include "mozilla/layers/RenderRootStateManager.h"
#include "mozilla/layers/WebRenderMessages.h"
#include "mozilla/layers/IpcResourceUpdateQueue.h"
#include "mozilla/layers/SharedSurfacesChild.h"
#include "mozilla/webgpu/WebGPUChild.h"
#include "nsDisplayListInvalidation.h"
#include "nsIFrame.h"
#include "WebRenderCanvasRenderer.h"
using namespace mozilla::image;
namespace mozilla {
namespace layers {
void WebRenderBackgroundData::AddWebRenderCommands(
wr::DisplayListBuilder& aBuilder) {
aBuilder.PushRect(mBounds, mBounds, true, true, false, mColor);
}
/* static */
bool WebRenderUserData::SupportsAsyncUpdate(nsIFrame* aFrame) {
if (!aFrame) {
return false;
}
RefPtr<WebRenderImageData> data = GetWebRenderUserData<WebRenderImageData>(
aFrame, static_cast<uint32_t>(DisplayItemType::TYPE_VIDEO));
if (data) {
return data->IsAsync();
}
return false;
}
/* static */
bool WebRenderUserData::ProcessInvalidateForImage(nsIFrame* aFrame,
DisplayItemType aType,
ImageProviderId aProviderId) {
MOZ_ASSERT(aFrame);
if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) {
aFrame->SchedulePaint();
return false;
}
auto type = static_cast<uint32_t>(aType);
RefPtr<WebRenderFallbackData> fallback =
GetWebRenderUserData<WebRenderFallbackData>(aFrame, type);
if (fallback) {
fallback->SetInvalid(true);
aFrame->SchedulePaint();
return true;
}
RefPtr<WebRenderImageProviderData> image =
GetWebRenderUserData<WebRenderImageProviderData>(aFrame, type);
if (image && image->Invalidate(aProviderId)) {
return true;
}
aFrame->SchedulePaint();
return false;
}
WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
uint32_t aDisplayItemKey, nsIFrame* aFrame)
: mManager(aManager),
mFrame(aFrame),
mDisplayItemKey(aDisplayItemKey),
mTable(aManager->GetWebRenderUserDataTable()),
mUsed(false) {}
WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: mManager(aManager),
mFrame(aItem->Frame()),
mDisplayItemKey(aItem->GetPerFrameKey()),
mTable(aManager->GetWebRenderUserDataTable()),
mUsed(false) {}
WebRenderUserData::~WebRenderUserData() = default;
void WebRenderUserData::RemoveFromTable() { mTable->Remove(this); }
WebRenderBridgeChild* WebRenderUserData::WrBridge() const {
return mManager->WrBridge();
}
WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
uint32_t aDisplayItemKey,
nsIFrame* aFrame)
: WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
WebRenderImageData::~WebRenderImageData() {
ClearImageKey();
if (mPipelineId) {
mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
}
}
void WebRenderImageData::ClearImageKey() {
if (mKey) {
mManager->AddImageKeyForDiscard(mKey.value());
if (mTextureOfImage) {
WrBridge()->ReleaseTextureOfImage(mKey.value());
mTextureOfImage = nullptr;
}
mKey.reset();
}
MOZ_ASSERT(!mTextureOfImage);
}
Maybe<wr::ImageKey> WebRenderImageData::UpdateImageKey(
ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources,
bool aFallback) {
MOZ_ASSERT(aContainer);
if (mContainer != aContainer) {
mContainer = aContainer;
}
CreateImageClientIfNeeded();
if (!mImageClient) {
return Nothing();
}
MOZ_ASSERT(mImageClient->AsImageClientSingle());
ImageClientSingle* imageClient = mImageClient->AsImageClientSingle();
uint32_t oldCounter = imageClient->GetLastUpdateGenerationCounter();
bool ret = imageClient->UpdateImage(aContainer);
RefPtr<TextureClient> currentTexture = imageClient->GetForwardedTexture();
if (!ret || !currentTexture) {
// Delete old key
ClearImageKey();
return Nothing();
}
// Reuse old key if generation is not updated.
if (!aFallback &&
oldCounter == imageClient->GetLastUpdateGenerationCounter() && mKey) {
return mKey;
}
// If we already had a texture and the format hasn't changed, better to reuse
// the image keys than create new ones.
bool useUpdate = mKey.isSome() && !!mTextureOfImage && !!currentTexture &&
mTextureOfImage->GetSize() == currentTexture->GetSize() &&
mTextureOfImage->GetFormat() == currentTexture->GetFormat();
wr::MaybeExternalImageId extId = currentTexture->GetExternalImageKey();
MOZ_RELEASE_ASSERT(extId.isSome());
if (useUpdate) {
MOZ_ASSERT(mKey.isSome());
MOZ_ASSERT(mTextureOfImage);
aResources.PushExternalImageForTexture(
extId.ref(), mKey.ref(), currentTexture, /* aIsUpdate */ true);
} else {
ClearImageKey();
wr::WrImageKey key = WrBridge()->GetNextImageKey();
aResources.PushExternalImageForTexture(extId.ref(), key, currentTexture,
/* aIsUpdate */ false);
mKey = Some(key);
}
mTextureOfImage = currentTexture;
return mKey;
}
already_AddRefed<ImageClient> WebRenderImageData::GetImageClient() {
RefPtr<ImageClient> imageClient = mImageClient;
return imageClient.forget();
}
void WebRenderImageData::CreateAsyncImageWebRenderCommands(
mozilla::wr::DisplayListBuilder& aBuilder, ImageContainer* aContainer,
const StackingContextHelper& aSc, const LayoutDeviceRect& aBounds,
const LayoutDeviceRect& aSCBounds, VideoInfo::Rotation aRotation,
const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode,
bool aIsBackfaceVisible) {
MOZ_ASSERT(aContainer->IsAsync());
if (mPipelineId.isSome() && mContainer != aContainer) {
// In this case, we need to remove the existed pipeline and create new one
// because the ImageContainer is changed.
WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
mPipelineId.reset();
}
if (!mPipelineId) {
// Alloc async image pipeline id.
mPipelineId =
Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
WrBridge()->AddPipelineIdForCompositable(
mPipelineId.ref(), aContainer->GetAsyncContainerHandle(),
CompositableHandleOwner::ImageBridge);
mContainer = aContainer;
}
MOZ_ASSERT(!mImageClient);
// Push IFrame for async image pipeline.
//
// We don't push a stacking context for this async image pipeline here.
// Instead, we do it inside the iframe that hosts the image. As a result,
// a bunch of the calculations normally done as part of that stacking
// context need to be done manually and pushed over to the parent side,
// where it will be done when we build the display list for the iframe.
// That happens in AsyncImagePipelineManager.
aBuilder.PushIFrame(aBounds, aIsBackfaceVisible, mPipelineId.ref(),
/*ignoreMissingPipelines*/ false);
WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(
mPipelineId.value(), aSCBounds, aRotation, aFilter, aMixBlendMode));
}
void WebRenderImageData::CreateImageClientIfNeeded() {
if (!mImageClient) {
mImageClient = ImageClient::CreateImageClient(
CompositableType::IMAGE, WrBridge(), TextureFlags::DEFAULT);
if (!mImageClient) {
return;
}
mImageClient->Connect();
}
}
WebRenderImageProviderData::WebRenderImageProviderData(
RenderRootStateManager* aManager, nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderImageProviderData::WebRenderImageProviderData(
RenderRootStateManager* aManager, uint32_t aDisplayItemKey,
nsIFrame* aFrame)
: WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
WebRenderImageProviderData::~WebRenderImageProviderData() = default;
Maybe<wr::ImageKey> WebRenderImageProviderData::UpdateImageKey(
WebRenderImageProvider* aProvider, ImgDrawResult aDrawResult,
wr::IpcResourceUpdateQueue& aResources) {
if (mProvider != aProvider) {
mProvider = aProvider;
}
wr::ImageKey key = {};
nsresult rv = mProvider ? mProvider->UpdateKey(mManager, aResources, key)
: NS_ERROR_FAILURE;
mKey = NS_SUCCEEDED(rv) ? Some(key) : Nothing();
mDrawResult = aDrawResult;
return mKey;
}
bool WebRenderImageProviderData::Invalidate(ImageProviderId aProviderId) const {
if (!aProviderId || !mProvider || mProvider->GetProviderId() != aProviderId ||
!mKey) {
return false;
}
if (mDrawResult != ImgDrawResult::SUCCESS &&
mDrawResult != ImgDrawResult::BAD_IMAGE) {
return false;
}
wr::ImageKey key = {};
nsresult rv =
mProvider->UpdateKey(mManager, mManager->AsyncResourceUpdates(), key);
return NS_SUCCEEDED(rv) && mKey.ref() == key;
}
WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem), mOpacity(1.0f), mInvalid(false) {}
WebRenderFallbackData::~WebRenderFallbackData() { ClearImageKey(); }
void WebRenderFallbackData::SetBlobImageKey(const wr::BlobImageKey& aKey) {
ClearImageKey();
mBlobKey = Some(aKey);
}
Maybe<wr::ImageKey> WebRenderFallbackData::GetImageKey() {
if (mBlobKey) {
return Some(wr::AsImageKey(mBlobKey.value()));
}
if (mImageData) {
return mImageData->GetImageKey();
}
return Nothing();
}
void WebRenderFallbackData::ClearImageKey() {
if (mImageData) {
mImageData->ClearImageKey();
mImageData = nullptr;
}
if (mBlobKey) {
mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
mBlobKey.reset();
}
}
WebRenderImageData* WebRenderFallbackData::PaintIntoImage() {
if (mBlobKey) {
mManager->AddBlobImageKeyForDiscard(mBlobKey.value());
mBlobKey.reset();
}
if (mImageData) {
return mImageData.get();
}
mImageData = MakeAndAddRef<WebRenderImageData>(mManager.get(),
mDisplayItemKey, mFrame);
return mImageData.get();
}
WebRenderAPZAnimationData::WebRenderAPZAnimationData(
RenderRootStateManager* aManager, nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem),
mAnimationId(AnimationHelper::GetNextCompositorAnimationsId()) {}
WebRenderAnimationData::WebRenderAnimationData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderAnimationData::~WebRenderAnimationData() {
// It may be the case that nsDisplayItem that created this WebRenderUserData
// gets destroyed without getting a chance to discard the compositor animation
// id, so we should do it as part of cleanup here.
uint64_t animationId = mAnimationInfo.GetCompositorAnimationsId();
// animationId might be 0 if mAnimationInfo never held any active animations.
if (animationId) {
mManager->AddCompositorAnimationsIdForDiscard(animationId);
}
}
WebRenderCanvasData::WebRenderCanvasData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderCanvasData::~WebRenderCanvasData() {
if (mCanvasRenderer) {
mCanvasRenderer->ClearCachedResources();
}
}
void WebRenderCanvasData::ClearCanvasRenderer() { mCanvasRenderer = nullptr; }
WebRenderCanvasRendererAsync* WebRenderCanvasData::GetCanvasRenderer() {
return mCanvasRenderer.get();
}
WebRenderCanvasRendererAsync* WebRenderCanvasData::CreateCanvasRenderer() {
mCanvasRenderer = new WebRenderCanvasRendererAsync(mManager);
return mCanvasRenderer.get();
}
bool WebRenderCanvasData::SetCanvasRenderer(CanvasRenderer* aCanvasRenderer) {
if (!aCanvasRenderer || !aCanvasRenderer->AsWebRenderCanvasRendererAsync()) {
return false;
}
auto* renderer = aCanvasRenderer->AsWebRenderCanvasRendererAsync();
if (mManager != renderer->GetRenderRootStateManager()) {
return false;
}
mCanvasRenderer = renderer;
return true;
}
void WebRenderCanvasData::SetImageContainer(ImageContainer* aImageContainer) {
mContainer = aImageContainer;
}
ImageContainer* WebRenderCanvasData::GetImageContainer() {
if (!mContainer) {
mContainer = MakeAndAddRef<ImageContainer>();
}
return mContainer;
}
void WebRenderCanvasData::ClearImageContainer() { mContainer = nullptr; }
WebRenderRemoteData::WebRenderRemoteData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderRemoteData::~WebRenderRemoteData() {
if (mRemoteBrowser) {
mRemoteBrowser->UpdateEffects(mozilla::dom::EffectsInfo::FullyHidden());
}
}
void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable) {
for (const auto& value : aTable->Values()) {
value->RemoveFromTable();
}
delete aTable;
}
} // namespace layers
} // namespace mozilla