Source code

Revision control

Other Tools

/* -*- 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 "BasicLayers.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"
namespace mozilla {
namespace layers {
void WebRenderBackgroundData::AddWebRenderCommands(
wr::DisplayListBuilder& aBuilder) {
aBuilder.PushRect(mBounds, mBounds, true, 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, ContainerProducerID aProducerId) {
MOZ_ASSERT(aFrame);
if (!aFrame->HasProperty(WebRenderUserDataProperty::Key())) {
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<WebRenderImageData> image =
GetWebRenderUserData<WebRenderImageData>(aFrame, type);
if (image && image->UsingSharedSurface(aProducerId)) {
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), mOwnsKey(false) {}
WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager,
uint32_t aDisplayItemKey,
nsIFrame* aFrame)
: WebRenderUserData(aManager, aDisplayItemKey, aFrame), mOwnsKey(false) {}
WebRenderImageData::~WebRenderImageData() {
ClearImageKey();
if (mPipelineId) {
mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
}
}
bool WebRenderImageData::UsingSharedSurface(
ContainerProducerID aProducerId) const {
if (!mContainer || !mKey || mOwnsKey) {
return false;
}
// If this is just an update with the same image key, then we know that the
// share request initiated an asynchronous update so that we don't need to
// rebuild the scene.
wr::ImageKey key;
nsresult rv = SharedSurfacesChild::Share(
mContainer, mManager, mManager->AsyncResourceUpdates(), key, aProducerId);
return NS_SUCCEEDED(rv) && mKey.ref() == key;
}
void WebRenderImageData::ClearImageKey() {
if (mKey) {
// If we don't own the key, then the owner is responsible for discarding the
// key when appropriate.
if (mOwnsKey) {
mManager->AddImageKeyForDiscard(mKey.value());
if (mTextureOfImage) {
WrBridge()->ReleaseTextureOfImage(mKey.value());
mTextureOfImage = nullptr;
}
}
mKey.reset();
}
mOwnsKey = false;
MOZ_ASSERT(!mTextureOfImage);
}
Maybe<wr::ImageKey> WebRenderImageData::UpdateImageKey(
ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources,
bool aFallback) {
MOZ_ASSERT(aContainer);
if (mContainer != aContainer) {
mContainer = aContainer;
}
wr::WrImageKey key;
if (!aFallback) {
nsresult rv = SharedSurfacesChild::Share(aContainer, mManager, aResources,
key, kContainerProducerID_Invalid);
if (NS_SUCCEEDED(rv)) {
// Ensure that any previously owned keys are released before replacing. We
// don't own this key, the surface itself owns it, so that it can be
// shared across multiple elements.
ClearImageKey();
mKey = Some(key);
return mKey;
}
if (rv != NS_ERROR_NOT_IMPLEMENTED) {
// We should be using the shared surface but somehow sharing it failed.
ClearImageKey();
return Nothing();
}
}
CreateImageClientIfNeeded();
if (!mImageClient) {
return Nothing();
}
MOZ_ASSERT(mImageClient->AsImageClientSingle());
ImageClientSingle* imageClient = mImageClient->AsImageClientSingle();
uint32_t oldCounter = imageClient->GetLastUpdateGenerationCounter();
bool ret = imageClient->UpdateImage(aContainer, /* unused */ 0);
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();
key = WrBridge()->GetNextImageKey();
aResources.PushExternalImageForTexture(extId.ref(), key, currentTexture,
/* aIsUpdate */ false);
mKey = Some(key);
}
mTextureOfImage = currentTexture;
mOwnsKey = true;
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()->AddPipelineIdForAsyncCompositable(
mPipelineId.ref(), aContainer->GetAsyncContainerHandle());
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.
wr::LayoutRect r = wr::ToLayoutRect(aBounds);
aBuilder.PushIFrame(r, 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();
}
}
WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager,
uint32_t aDisplayItemKey,
nsIFrame* aFrame)
: WebRenderUserData(aManager, aDisplayItemKey, aFrame) {}
Maybe<wr::BlobImageKey> WebRenderBlobImageData::UpdateImageKey(
ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources) {
MOZ_ASSERT(aContainer);
if (mContainer != aContainer) {
mContainer = aContainer;
}
wr::BlobImageKey key = {};
nsresult rv =
SharedSurfacesChild::ShareBlob(aContainer, mManager, aResources, key);
if (NS_SUCCEEDED(rv)) {
mKey = Some(key);
} else {
mKey.reset();
}
return mKey;
}
WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager,
nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem), 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();
}
void WebRenderCanvasData::SetImageContainer(ImageContainer* aImageContainer) {
mContainer = aImageContainer;
}
ImageContainer* WebRenderCanvasData::GetImageContainer() {
if (!mContainer) {
mContainer = LayerManager::CreateImageContainer();
}
return mContainer;
}
void WebRenderCanvasData::ClearImageContainer() { mContainer = nullptr; }
WebRenderLocalCanvasData::WebRenderLocalCanvasData(
RenderRootStateManager* aManager, nsDisplayItem* aItem)
: WebRenderUserData(aManager, aItem) {}
WebRenderLocalCanvasData::~WebRenderLocalCanvasData() = default;
void WebRenderLocalCanvasData::RequestFrameReadback() {
if (mGpuBridge) {
mGpuBridge->SwapChainPresent(mExternalImageId, mGpuTextureId);
}
}
void WebRenderLocalCanvasData::RefreshExternalImage() {
if (!mDirty) {
return;
}
const ImageIntRect dirtyRect(0, 0, mDescriptor.width, mDescriptor.height);
// Update the WR external image, forcing the composition of a new frame.
mManager->AsyncResourceUpdates().UpdatePrivateExternalImage(
mExternalImageId, mImageKey, mDescriptor, dirtyRect);
mDirty = false;
}
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