diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/layers/wr/WebRenderUserData.cpp | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/gfx/layers/wr/WebRenderUserData.cpp b/gfx/layers/wr/WebRenderUserData.cpp new file mode 100644 index 0000000000..b9f885d6b6 --- /dev/null +++ b/gfx/layers/wr/WebRenderUserData.cpp @@ -0,0 +1,432 @@ +/* -*- 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->RemoveEntry(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(); + } +} + +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 (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) { + iter.UserData()->RemoveFromTable(); + } + delete aTable; +} + +} // namespace layers +} // namespace mozilla |