/* -*- 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 data = GetWebRenderUserData( aFrame, static_cast(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(aType); RefPtr fallback = GetWebRenderUserData(aFrame, type); if (fallback) { fallback->SetInvalid(true); aFrame->SchedulePaint(); return true; } RefPtr image = GetWebRenderUserData(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 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 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 WebRenderImageData::GetImageClient() { RefPtr 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 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(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