--- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -23,16 +23,17 @@ #include "mozilla/ipc/CrossProcessMutex.h" // for CrossProcessMutex, etc #include "mozilla/layers/CompositorTypes.h" #include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild #include "mozilla/layers/ImageClient.h" // for ImageClient #include "mozilla/layers/ImageDataSerializer.h" // for SurfaceDescriptorBuffer #include "mozilla/layers/LayersMessages.h" #include "mozilla/layers/SharedPlanarYCbCrImage.h" #include "mozilla/layers/SharedRGBImage.h" +#include "mozilla/layers/SharedSurfacesChild.h" // for SharedSurfacesAnimation #include "mozilla/layers/TextureClientRecycleAllocator.h" #include "nsProxyRelease.h" #include "nsISupportsUtils.h" // for NS_IF_ADDREF #ifdef XP_MACOSX # include "MacIOSurfaceImage.h" # include "mozilla/gfx/QuartzSupport.h" #endif @@ -169,16 +170,23 @@ void ImageContainer::EnsureImageClient() } else { // It's okay to drop the async container handle since the ImageBridgeChild // is going to die anyway. mAsyncContainerHandle = CompositableHandle(); } } } +SharedSurfacesAnimation* ImageContainer::EnsureSharedSurfacesAnimation() { + if (!mSharedAnimation) { + mSharedAnimation = new SharedSurfacesAnimation(); + } + return mSharedAnimation; +} + ImageContainer::ImageContainer(Mode flag) : mRecursiveMutex("ImageContainer.mRecursiveMutex"), mGenerationCounter(++sGenerationCounter), mPaintCount(0), mDroppedImageCount(0), mImageFactory(new ImageFactory()), mRecycleBin(new BufferRecycleBin()), mIsAsync(flag == ASYNCHRONOUS), @@ -207,16 +215,19 @@ ImageContainer::~ImageContainer() { mNotifyCompositeListener->ClearImageContainer(); } if (mAsyncContainerHandle) { if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) { imageBridge->ForgetImageContainer(mAsyncContainerHandle); } } + if (mSharedAnimation) { + mSharedAnimation->Destroy(); + } } Maybe<SurfaceDescriptor> Image::GetDesc() { return {}; } Maybe<SurfaceDescriptor> Image::GetDescFromTexClient( TextureClient* const forTc) { RefPtr<TextureClient> tc = forTc; if (!forTc) {
--- a/gfx/layers/ImageContainer.h +++ b/gfx/layers/ImageContainer.h @@ -43,16 +43,17 @@ namespace mozilla { namespace layers { class ImageClient; class ImageCompositeNotification; class ImageContainer; class ImageContainerChild; class SharedPlanarYCbCrImage; +class SharedSurfacesAnimation; class SurfaceDescriptor; class PlanarYCbCrImage; class TextureClient; class TextureClientRecycleAllocator; class KnowsCompositor; class NVImage; class MemoryOrShmem; #ifdef XP_WIN @@ -523,16 +524,22 @@ class ImageContainer final : public Supp /** * Main thread only. */ static ProducerID AllocateProducerID(); void DropImageClient(); + SharedSurfacesAnimation* GetSharedSurfacesAnimation() const { + return mSharedAnimation; + } + + SharedSurfacesAnimation* EnsureSharedSurfacesAnimation(); + private: typedef mozilla::RecursiveMutex RecursiveMutex; void SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages); // This is called to ensure we have an active image, this may not be true // when we're storing image information in a RemoteImageData structure. // NOTE: If we have remote data mRemoteDataMutex should be locked when @@ -587,16 +594,18 @@ class ImageContainer final : public Supp // sucessfully created with ENABLE_ASYNC, or points to null otherwise. // 'unsuccessful' in this case only means that the ImageClient could not // be created, most likely because off-main-thread compositing is not enabled. // In this case the ImageContainer is perfectly usable, but it will forward // frames to the compositor through transactions in the main thread rather // than asynchronusly using the ImageBridge IPDL protocol. RefPtr<ImageClient> mImageClient; + RefPtr<SharedSurfacesAnimation> mSharedAnimation; + bool mIsAsync; CompositableHandle mAsyncContainerHandle; // ProducerID for last current image(s) ProducerID mCurrentProducerID; RefPtr<ImageContainerListener> mNotifyCompositeListener;
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp +++ b/gfx/layers/ipc/SharedSurfacesChild.cpp @@ -3,16 +3,17 @@ /* 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 "SharedSurfacesChild.h" #include "SharedSurfacesParent.h" #include "CompositorManagerChild.h" #include "mozilla/gfx/gfxVars.h" +#include "mozilla/image/SourceSurfaceBlobImage.h" #include "mozilla/layers/IpcResourceUpdateQueue.h" #include "mozilla/layers/SourceSurfaceSharedData.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/StaticPrefs_image.h" namespace mozilla { @@ -319,16 +320,100 @@ nsresult SharedSurfacesChild::Share(Sour if (!sharedSurface) { return NS_ERROR_NOT_IMPLEMENTED; } return Share(sharedSurface, aManager, aResources, aKey); } /* static */ +nsresult SharedSurfacesChild::Share(ImageContainer* aContainer, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey, + ContainerProducerID aProducerId) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContainer); + MOZ_ASSERT(aManager); + + if (aContainer->IsAsync()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + AutoTArray<ImageContainer::OwningImage, 4> images; + aContainer->GetCurrentImages(&images); + if (images.IsEmpty()) { + return NS_ERROR_NOT_AVAILABLE; + } + + if (aProducerId != kContainerProducerID_Invalid && + images[0].mProducerID != aProducerId) { + // If the producer ID of the surface in the container does not match the + // expected producer ID, then we do not want to proceed with sharing. This + // is useful for when callers are unsure if given container is for the same + // producer / underlying image request. + return NS_ERROR_FAILURE; + } + + RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface(); + if (!surface) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + auto sharedSurface = AsSourceSurfaceSharedData(surface); + if (!sharedSurface) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + SharedSurfacesAnimation* anim = aContainer->GetSharedSurfacesAnimation(); + if (anim) { + return anim->UpdateKey(sharedSurface, aManager, aResources, aKey); + } + + return Share(sharedSurface, aManager, aResources, aKey); +} + +/* static */ +nsresult SharedSurfacesChild::ShareBlob(ImageContainer* aContainer, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::BlobImageKey& aKey) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContainer); + MOZ_ASSERT(aManager); + + if (aContainer->IsAsync()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + AutoTArray<ImageContainer::OwningImage, 4> images; + aContainer->GetCurrentImages(&images); + if (images.IsEmpty()) { + return NS_ERROR_NOT_AVAILABLE; + } + + RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface(); + if (!surface || surface->GetType() != SurfaceType::BLOB_IMAGE) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + auto* blobSurface = + static_cast<image::SourceSurfaceBlobImage*>(surface.get()); + + Maybe<wr::BlobImageKey> key = + blobSurface->UpdateKey(aManager->LayerManager(), aResources); + if (!key) { + return NS_ERROR_FAILURE; + } + + aKey = key.value(); + return NS_OK; +} + +/* static */ nsresult SharedSurfacesChild::Share(SourceSurface* aSurface, wr::ExternalImageId& aId) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aSurface); auto sharedSurface = AsSourceSurfaceSharedData(aSurface); if (!sharedSurface) { return NS_ERROR_NOT_IMPLEMENTED; @@ -387,16 +472,38 @@ void SharedSurfacesChild::Unshare(const static_cast<SharedUserData*>(aSurface->GetUserData(&sSharedKey)); if (!data || !data->IsShared()) { return Nothing(); } return Some(data->Id()); } +/* static */ +nsresult SharedSurfacesChild::UpdateAnimation(ImageContainer* aContainer, + SourceSurface* aSurface, + const IntRect& aDirtyRect) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContainer); + MOZ_ASSERT(!aContainer->IsAsync()); + MOZ_ASSERT(aSurface); + + // If we aren't using shared surfaces, then is nothing to do. + auto sharedSurface = SharedSurfacesChild::AsSourceSurfaceSharedData(aSurface); + if (!sharedSurface) { + MOZ_ASSERT(!aContainer->GetSharedSurfacesAnimation()); + return NS_ERROR_NOT_IMPLEMENTED; + } + + SharedSurfacesAnimation* anim = aContainer->EnsureSharedSurfacesAnimation(); + MOZ_ASSERT(anim); + + return anim->SetCurrentFrame(sharedSurface, aDirtyRect); +} + AnimationImageKeyData::AnimationImageKeyData(RenderRootStateManager* aManager, const wr::ImageKey& aImageKey) : SharedSurfacesChild::ImageKeyData(aManager, aImageKey) {} AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther) : SharedSurfacesChild::ImageKeyData(std::move(aOther)), mPendingRelease(std::move(aOther.mPendingRelease)) {}
--- a/gfx/layers/ipc/SharedSurfacesChild.h +++ b/gfx/layers/ipc/SharedSurfacesChild.h @@ -39,16 +39,17 @@ class SourceSurfaceSharedData; namespace wr { class IpcResourceUpdateQueue; } // namespace wr namespace layers { class CompositorManagerChild; +class ImageContainer; class RenderRootStateManager; class SharedSurfacesChild { public: /** * Request that the surface be mapped into the compositor thread's memory * space. This is useful for when the caller itself has no present need for * the surface to be mapped, but knows there will be such a need in the @@ -80,29 +81,50 @@ class SharedSurfacesChild { * This must be called from the main thread. */ static nsresult Share(gfx::SourceSurface* aSurface, RenderRootStateManager* aManager, wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey); /** + * Request that the first surface in the image container's current images be + * mapped into the compositor thread's memory space, and a valid ImageKey be + * generated for it for use with WebRender. If a different method should be + * used to share the image data for this particular container, it will return + * NS_ERROR_NOT_IMPLEMENTED. This must be called from the main thread. + */ + static nsresult Share(ImageContainer* aContainer, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::ImageKey& aKey, ContainerProducerID aProducerId); + + static nsresult ShareBlob(ImageContainer* aContainer, + RenderRootStateManager* aManager, + wr::IpcResourceUpdateQueue& aResources, + wr::BlobImageKey& aKey); + + /** * Get the external ID, if any, bound to the shared surface. Used for memory * reporting purposes. */ static Maybe<wr::ExternalImageId> GetExternalId( const gfx::SourceSurfaceSharedData* aSurface); /** * Get the surface (or its underlying surface) as a SourceSurfaceSharedData * pointer, if valid. */ static gfx::SourceSurfaceSharedData* AsSourceSurfaceSharedData( gfx::SourceSurface* aSurface); + static nsresult UpdateAnimation(ImageContainer* aContainer, + gfx::SourceSurface* aSurface, + const gfx::IntRect& aDirtyRect); + class ImageKeyData { public: ImageKeyData(RenderRootStateManager* aManager, const wr::ImageKey& aImageKey); virtual ~ImageKeyData(); ImageKeyData(ImageKeyData&& aOther); ImageKeyData& operator=(ImageKeyData&& aOther);
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp +++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp @@ -12,17 +12,16 @@ #include "mozilla/EffectCompositor.h" #include "mozilla/ProfilerLabels.h" #include "mozilla/StaticPrefs_gfx.h" #include "mozilla/SVGGeometryFrame.h" #include "mozilla/UniquePtr.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/Types.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/AnimationHelper.h" #include "mozilla/layers/ClipManager.h" #include "mozilla/layers/ImageClient.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/layers/IpcResourceUpdateQueue.h" #include "mozilla/layers/SharedSurfacesChild.h" @@ -36,17 +35,16 @@ #include "nsLayoutUtils.h" #include "nsTHashSet.h" #include "WebRenderCanvasRenderer.h" namespace mozilla { namespace layers { using namespace gfx; -using namespace image; static int sIndent; #include <stdarg.h> #include <stdio.h> static void GP(const char* fmt, ...) { va_list args; va_start(args, fmt); #if 0 @@ -2006,40 +2004,43 @@ bool WebRenderCommandBuilder::PushImage( auto r = wr::ToLayoutRect(aRect); auto c = wr::ToLayoutRect(aClip); aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, key.value()); return true; } -Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageProviderKey( - nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, +Maybe<wr::BlobImageKey> WebRenderCommandBuilder::CreateBlobImageKey( + nsDisplayItem* aItem, ImageContainer* aContainer, mozilla::wr::IpcResourceUpdateQueue& aResources) { - RefPtr<WebRenderImageProviderData> imageData = - CreateOrRecycleWebRenderUserData<WebRenderImageProviderData>(aItem); + MOZ_ASSERT(!aContainer->IsAsync()); + + RefPtr<WebRenderBlobImageData> imageData = + CreateOrRecycleWebRenderUserData<WebRenderBlobImageData>(aItem); MOZ_ASSERT(imageData); - return imageData->UpdateImageKey(aProvider, aResources); + return imageData->UpdateImageKey(aContainer, aResources); } -bool WebRenderCommandBuilder::PushImageProvider( - nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, +bool WebRenderCommandBuilder::PushBlobImage( + nsDisplayItem* aItem, ImageContainer* aContainer, mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip) { - Maybe<wr::ImageKey> key = - CreateImageProviderKey(aItem, aProvider, aResources); + Maybe<wr::BlobImageKey> key = + CreateBlobImageKey(aItem, aContainer, aResources); if (!key) { return false; } auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); auto r = wr::ToLayoutRect(aRect); auto c = wr::ToLayoutRect(aClip); - aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, key.value()); + aBuilder.PushImage(r, c, !aItem->BackfaceIsHidden(), rendering, + wr::AsImageKey(key.value())); return true; } static void PaintItemByDrawTarget(nsDisplayItem* aItem, gfx::DrawTarget* aDT, const LayoutDevicePoint& aOffset, const IntRect& visibleRect, nsDisplayListBuilder* aDisplayListBuilder,
--- a/gfx/layers/wr/WebRenderCommandBuilder.h +++ b/gfx/layers/wr/WebRenderCommandBuilder.h @@ -16,20 +16,16 @@ #include "mozilla/SVGIntegrationUtils.h" // for WrFiltersHolder #include "nsDisplayList.h" #include "nsIFrame.h" #include "nsTHashSet.h" #include "DisplayItemCache.h" namespace mozilla { -namespace image { -class WebRenderImageProvider; -} - namespace layers { class ImageClient; class ImageContainer; class WebRenderBridgeChild; class WebRenderCanvasData; class WebRenderCanvasRendererAsync; class WebRenderImageData; @@ -64,36 +60,35 @@ class WebRenderCommandBuilder final { Maybe<wr::ImageKey> CreateImageKey( nsDisplayItem* aItem, ImageContainer* aContainer, mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc, gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds); - Maybe<wr::ImageKey> CreateImageProviderKey( - nsDisplayItem* aItem, image::WebRenderImageProvider* aProvider, + Maybe<wr::BlobImageKey> CreateBlobImageKey( + nsDisplayItem* aItem, ImageContainer* aContainer, mozilla::wr::IpcResourceUpdateQueue& aResources); WebRenderUserDataRefTable* GetWebRenderUserDataTable() { return &mWebRenderUserDatas; } bool PushImage(nsDisplayItem* aItem, ImageContainer* aContainer, mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, const LayoutDeviceRect& aRect, const LayoutDeviceRect& aClip); - bool PushImageProvider(nsDisplayItem* aItem, - image::WebRenderImageProvider* aProvider, - mozilla::wr::DisplayListBuilder& aBuilder, - mozilla::wr::IpcResourceUpdateQueue& aResources, - const LayoutDeviceRect& aRect, - const LayoutDeviceRect& aClip); + bool PushBlobImage(nsDisplayItem* aItem, ImageContainer* aContainer, + mozilla::wr::DisplayListBuilder& aBuilder, + mozilla::wr::IpcResourceUpdateQueue& aResources, + const LayoutDeviceRect& aRect, + const LayoutDeviceRect& aClip); Maybe<wr::ImageMask> BuildWrMaskImage( nsDisplayMasksAndClipPaths* aMaskItem, wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources, const StackingContextHelper& aSc, nsDisplayListBuilder* aDisplayListBuilder, const LayoutDeviceRect& aBounds); bool PushItemAsImage(nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
--- a/gfx/layers/wr/WebRenderUserData.cpp +++ b/gfx/layers/wr/WebRenderUserData.cpp @@ -1,32 +1,29 @@ /* -*- 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, mColor); } @@ -40,37 +37,36 @@ bool WebRenderUserData::SupportsAsyncUpd if (data) { return data->IsAsync(); } return false; } /* static */ -bool WebRenderUserData::ProcessInvalidateForImage(nsIFrame* aFrame, - DisplayItemType aType, - ImageProviderId aProviderId) { +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<WebRenderImageProviderData> image = - GetWebRenderUserData<WebRenderImageProviderData>(aFrame, type); - if (image && image->Invalidate(aProviderId)) { + RefPtr<WebRenderImageData> image = + GetWebRenderUserData<WebRenderImageData>(aFrame, type); + if (image && image->UsingSharedSurface(aProducerId)) { return true; } aFrame->SchedulePaint(); return false; } WebRenderUserData::WebRenderUserData(RenderRootStateManager* aManager, @@ -94,52 +90,92 @@ WebRenderUserData::~WebRenderUserData() void WebRenderUserData::RemoveFromTable() { mTable->Remove(this); } WebRenderBridgeChild* WebRenderUserData::WrBridge() const { return mManager->WrBridge(); } WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager, nsDisplayItem* aItem) - : WebRenderUserData(aManager, aItem) {} + : WebRenderUserData(aManager, aItem), mOwnsKey(false) {} WebRenderImageData::WebRenderImageData(RenderRootStateManager* aManager, uint32_t aDisplayItemKey, nsIFrame* aFrame) - : WebRenderUserData(aManager, aDisplayItemKey, 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) { - mManager->AddImageKeyForDiscard(mKey.value()); - if (mTextureOfImage) { - WrBridge()->ReleaseTextureOfImage(mKey.value()); - mTextureOfImage = nullptr; + // 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(); @@ -170,23 +206,25 @@ Maybe<wr::ImageKey> WebRenderImageData:: 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(); + 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(); } @@ -238,53 +276,43 @@ void WebRenderImageData::CreateImageClie if (!mImageClient) { return; } mImageClient->Connect(); } } -WebRenderImageProviderData::WebRenderImageProviderData( - RenderRootStateManager* aManager, nsDisplayItem* aItem) +WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager, + nsDisplayItem* aItem) : WebRenderUserData(aManager, aItem) {} -WebRenderImageProviderData::WebRenderImageProviderData( - RenderRootStateManager* aManager, uint32_t aDisplayItemKey, - nsIFrame* aFrame) +WebRenderBlobImageData::WebRenderBlobImageData(RenderRootStateManager* aManager, + uint32_t aDisplayItemKey, + nsIFrame* aFrame) : WebRenderUserData(aManager, aDisplayItemKey, aFrame) {} -WebRenderImageProviderData::~WebRenderImageProviderData() = default; +Maybe<wr::BlobImageKey> WebRenderBlobImageData::UpdateImageKey( + ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources) { + MOZ_ASSERT(aContainer); -Maybe<wr::ImageKey> WebRenderImageProviderData::UpdateImageKey( - WebRenderImageProvider* aProvider, wr::IpcResourceUpdateQueue& aResources) { - MOZ_ASSERT(aProvider); - - if (mProvider != aProvider) { - mProvider = aProvider; + if (mContainer != aContainer) { + mContainer = aContainer; } - wr::ImageKey key = {}; - nsresult rv = mProvider->UpdateKey(mManager, aResources, key); - if (NS_FAILED(rv)) { - return Nothing(); + wr::BlobImageKey key = {}; + nsresult rv = + SharedSurfacesChild::ShareBlob(aContainer, mManager, aResources, key); + if (NS_SUCCEEDED(rv)) { + mKey = Some(key); + } else { + mKey.reset(); } - return Some(key); -} - -bool WebRenderImageProviderData::Invalidate(ImageProviderId aProviderId) const { - if (!aProviderId || mProvider->GetProviderId() != aProviderId) { - return false; - } - - wr::ImageKey key = {}; - nsresult rv = - mProvider->UpdateKey(mManager, mManager->AsyncResourceUpdates(), key); - return NS_SUCCEEDED(rv); + return mKey; } WebRenderFallbackData::WebRenderFallbackData(RenderRootStateManager* aManager, nsDisplayItem* aItem) : WebRenderUserData(aManager, aItem), mOpacity(1.0f), mInvalid(false) {} WebRenderFallbackData::~WebRenderFallbackData() { ClearImageKey(); }
--- a/gfx/layers/wr/WebRenderUserData.h +++ b/gfx/layers/wr/WebRenderUserData.h @@ -4,17 +4,16 @@ * 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/. */ #ifndef GFX_WEBRENDERUSERDATA_H #define GFX_WEBRENDERUSERDATA_H #include <vector> #include "mozilla/webrender/WebRenderAPI.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/AnimationInfo.h" #include "mozilla/dom/RemoteBrowser.h" #include "mozilla/UniquePtr.h" #include "nsIFrame.h" #include "nsRefPtrHashtable.h" #include "nsTHashSet.h" #include "ImageTypes.h" #include "DisplayItemClip.h" @@ -40,17 +39,16 @@ namespace layers { class BasicLayerManager; class ImageClient; class ImageContainer; class WebRenderBridgeChild; class WebRenderCanvasData; class WebRenderCanvasRenderer; class WebRenderCanvasRendererAsync; class WebRenderImageData; -class WebRenderImageProviderData; class WebRenderFallbackData; class WebRenderLocalCanvasData; class RenderRootStateManager; class WebRenderGroupData; class WebRenderBackgroundData { public: WebRenderBackgroundData(wr::LayoutRect aBounds, wr::ColorF aColor) @@ -66,42 +64,41 @@ class WebRenderBackgroundData { /// to an nsFrame. class WebRenderUserData { public: typedef nsTHashSet<RefPtr<WebRenderUserData>> WebRenderUserDataRefTable; static bool SupportsAsyncUpdate(nsIFrame* aFrame); static bool ProcessInvalidateForImage(nsIFrame* aFrame, DisplayItemType aType, - image::ImageProviderId aProviderId); + ContainerProducerID aProducerId); NS_INLINE_DECL_REFCOUNTING(WebRenderUserData) WebRenderUserData(RenderRootStateManager* aManager, nsDisplayItem* aItem); WebRenderUserData(RenderRootStateManager* aManager, uint32_t mDisplayItemKey, nsIFrame* aFrame); virtual WebRenderImageData* AsImageData() { return nullptr; } - virtual WebRenderImageProviderData* AsImageProviderData() { return nullptr; } virtual WebRenderFallbackData* AsFallbackData() { return nullptr; } virtual WebRenderCanvasData* AsCanvasData() { return nullptr; } virtual WebRenderLocalCanvasData* AsLocalCanvasData() { return nullptr; } virtual WebRenderGroupData* AsGroupData() { return nullptr; } enum class UserDataType { eImage, eFallback, eAPZAnimation, eAnimation, eCanvas, eLocalCanvas, eRemote, eGroup, eMask, - eImageProvider, // ImageLib + eBlobImage, // SVG image }; virtual UserDataType GetType() = 0; bool IsUsed() { return mUsed; } void SetUsed(bool aUsed) { mUsed = aUsed; } nsIFrame* GetFrame() { return mFrame; } uint32_t GetDisplayItemKey() { return mDisplayItemKey; } void RemoveFromTable(); @@ -167,47 +164,53 @@ class WebRenderImageData : public WebRen const LayoutDeviceRect& aSCBounds, VideoInfo::Rotation aRotation, const wr::ImageRendering& aFilter, const wr::MixBlendMode& aMixBlendMode, bool aIsBackfaceVisible); void CreateImageClientIfNeeded(); bool IsAsync() { return mPipelineId.isSome(); } + bool UsingSharedSurface(ContainerProducerID aProducerId) const; + void ClearImageKey(); protected: Maybe<wr::ImageKey> mKey; RefPtr<TextureClient> mTextureOfImage; RefPtr<ImageClient> mImageClient; Maybe<wr::PipelineId> mPipelineId; RefPtr<ImageContainer> mContainer; + // The key can be owned by a shared surface that is used by several elements. + // when this is the case the shared surface is responsible for managing the + // destruction of the key. + // TODO: we surely can come up with a simpler/safer way to model this. + bool mOwnsKey; }; -/// Holds some data used to share ImageLib results with the parent process. -/// This may be either in the form of a blob recording or a rasterized surface. -class WebRenderImageProviderData final : public WebRenderUserData { +/// Holds some data used to share blob recordings from VectorImages with the +/// parent process. +class WebRenderBlobImageData : public WebRenderUserData { public: - WebRenderImageProviderData(RenderRootStateManager* aManager, - nsDisplayItem* aItem); - WebRenderImageProviderData(RenderRootStateManager* aManager, - uint32_t aDisplayItemKey, nsIFrame* aFrame); - ~WebRenderImageProviderData() override; + WebRenderBlobImageData(RenderRootStateManager* aManager, + nsDisplayItem* aItem); + WebRenderBlobImageData(RenderRootStateManager* aManager, + uint32_t aDisplayItemKey, nsIFrame* aFrame); + virtual ~WebRenderBlobImageData() {} - WebRenderImageProviderData* AsImageProviderData() override { return this; } - UserDataType GetType() override { return UserDataType::eImageProvider; } - static UserDataType Type() { return UserDataType::eImageProvider; } + UserDataType GetType() override { return UserDataType::eBlobImage; } + static UserDataType Type() { return UserDataType::eBlobImage; } + Maybe<wr::BlobImageKey> GetImageKey() { return mKey; } - Maybe<wr::ImageKey> UpdateImageKey(image::WebRenderImageProvider* aProvider, - wr::IpcResourceUpdateQueue& aResources); - - bool Invalidate(image::ImageProviderId aProviderId) const; + Maybe<wr::BlobImageKey> UpdateImageKey( + ImageContainer* aContainer, wr::IpcResourceUpdateQueue& aResources); protected: - RefPtr<image::WebRenderImageProvider> mProvider; + Maybe<wr::BlobImageKey> mKey; + RefPtr<ImageContainer> mContainer; }; /// Used for fallback rendering. /// /// In most cases this uses blob images but it can also render on the content /// side directly into a texture. class WebRenderFallbackData : public WebRenderUserData { public:
--- a/image/AnimationFrameBuffer.cpp +++ b/image/AnimationFrameBuffer.cpp @@ -55,17 +55,16 @@ bool AnimationFrameRetainedBuffer::Reset // Either the decoder is still running, or we have enough frames already. // No need for us to restart it. return false; } bool AnimationFrameRetainedBuffer::MarkComplete( const gfx::IntRect& aFirstFrameRefreshArea) { MOZ_ASSERT(!mSizeKnown); - mFirstFrameRefreshArea = aFirstFrameRefreshArea; mSizeKnown = true; mPending = 0; mFrames.Compact(); return false; } void AnimationFrameRetainedBuffer::AdvanceInternal() { // We should not have advanced if we never inserted. @@ -188,21 +187,16 @@ bool AnimationFrameDiscardingQueue::Rese bool AnimationFrameDiscardingQueue::MarkComplete( const gfx::IntRect& aFirstFrameRefreshArea) { if (NS_WARN_IF(mInsertIndex != mSize)) { mRedecodeError = true; mPending = 0; } - // If we encounter a redecode error, just make the first frame refresh area to - // be the full frame, because we don't really know what we can safely recycle. - mFirstFrameRefreshArea = - mRedecodeError ? mFirstFrame->GetRect() : aFirstFrameRefreshArea; - // We reached the end of the animation, the next frame we get, if we get // another, will be the first frame again. mInsertIndex = 0; mSizeKnown = true; // Since we only request advancing when we want to resume at a certain point // in the animation, we should never exceed the number of frames. MOZ_ASSERT(mAdvance == 0); @@ -462,10 +456,22 @@ RawAccessFrameRef AnimationFrameRecyclin } } else { mRecycle.pop_front(); } return recycledFrame; } +bool AnimationFrameRecyclingQueue::MarkComplete( + const gfx::IntRect& aFirstFrameRefreshArea) { + bool continueDecoding = + AnimationFrameDiscardingQueue::MarkComplete(aFirstFrameRefreshArea); + + // If we encounter a redecode error, just make the first frame refresh area to + // be the full frame, because we don't really know what we can safely recycle. + mFirstFrameRefreshArea = + mRedecodeError ? mFirstFrame->GetRect() : aFirstFrameRefreshArea; + return continueDecoding; +} + } // namespace image } // namespace mozilla
--- a/image/AnimationFrameBuffer.h +++ b/image/AnimationFrameBuffer.h @@ -99,24 +99,16 @@ class AnimationFrameBuffer { /** * @returns The total number of frames in the animation. If SizeKnown() is * true, then this is a constant, else it is just the total number of * frames we have decoded thus far. */ size_t Size() const { return mSize; } /** - * @returns The first frame refresh area. This is used instead of the dirty - * rect for the last frame when transitioning back to the first frame. - */ - const gfx::IntRect& FirstFrameRefreshArea() const { - return mFirstFrameRefreshArea; - } - - /** * @returns True if encountered an error during redecode which should cause * the caller to stop inserting frames. */ bool HasRedecodeError() const { return mRedecodeError; } /** * @returns The current frame index we have advanced to. */ @@ -289,20 +281,16 @@ class AnimationFrameBuffer { /** * Discard any frames as necessary for the reset. mPending may be adjusted to * request more frames. * * @returns True if the caller should resume decoding new frames, else false. */ virtual bool ResetInternal() = 0; - /// The first frame refresh area. This is used instead of the dirty rect for - /// the last frame when transitioning back to the first frame. - gfx::IntRect mFirstFrameRefreshArea; - // The total number of frames in the animation. If mSizeKnown is true, it is // the actual total regardless of how many frames are available, otherwise it // is the total number of inserted frames. size_t mSize; // The minimum number of frames that we want buffered ahead of the display. size_t mBatch; @@ -427,16 +415,17 @@ class AnimationFrameDiscardingQueue : pu * so that the buffers are all identically sized and contain the complete frame * data. */ class AnimationFrameRecyclingQueue final : public AnimationFrameDiscardingQueue { public: explicit AnimationFrameRecyclingQueue(AnimationFrameRetainedBuffer&& aQueue); + bool MarkComplete(const gfx::IntRect& aFirstFrameRefreshArea) override; void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) override; RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override; struct RecycleEntry { explicit RecycleEntry(const gfx::IntRect& aDirtyRect) : mDirtyRect(aDirtyRect) {} @@ -453,26 +442,33 @@ class AnimationFrameRecyclingQueue final RecycleEntry(const RecycleEntry& aOther) = delete; RecycleEntry& operator=(const RecycleEntry& aOther) = delete; RefPtr<imgFrame> mFrame; // The frame containing the buffer to recycle. gfx::IntRect mDirtyRect; // The dirty rect of the frame itself. }; const std::deque<RecycleEntry>& Recycle() const { return mRecycle; } + const gfx::IntRect& FirstFrameRefreshArea() const { + return mFirstFrameRefreshArea; + } protected: void AdvanceInternal() override; bool ResetInternal() override; /// Queue storing frames to be recycled by the decoder to produce its future /// frames. May contain up to mBatch frames, where the last frame in the queue /// is adjacent to the first frame in the mDisplay queue. std::deque<RecycleEntry> mRecycle; + /// The first frame refresh area. This is used instead of the dirty rect for + /// the last frame when transitioning back to the first frame. + gfx::IntRect mFirstFrameRefreshArea; + /// Force recycled frames to use the first frame refresh area as their dirty /// rect. This is used when we are recycling frames from the end of an /// animation to produce frames at the beginning of an animation. bool mForceUseFirstFrameRefreshArea; }; } // namespace image } // namespace mozilla
--- a/image/AnimationSurfaceProvider.cpp +++ b/image/AnimationSurfaceProvider.cpp @@ -2,40 +2,35 @@ /* 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 "AnimationSurfaceProvider.h" #include "mozilla/StaticPrefs_image.h" #include "mozilla/gfx/gfxVars.h" -#include "mozilla/layers/SharedSurfacesChild.h" -#include "mozilla/layers/SourceSurfaceSharedData.h" #include "nsProxyRelease.h" #include "DecodePool.h" #include "Decoder.h" using namespace mozilla::gfx; -using namespace mozilla::layers; namespace mozilla { namespace image { AnimationSurfaceProvider::AnimationSurfaceProvider( NotNull<RasterImage*> aImage, const SurfaceKey& aSurfaceKey, NotNull<Decoder*> aDecoder, size_t aCurrentFrame) : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey, AvailabilityState::StartAsPlaceholder()), mImage(aImage.get()), mDecodingMutex("AnimationSurfaceProvider::mDecoder"), mDecoder(aDecoder.get()), - mFramesMutex("AnimationSurfaceProvider::mFrames"), - mCompositedFrameRequested(false), - mSharedAnimation(MakeRefPtr<SharedSurfacesAnimation>()) { + mFramesMutex("AnimationSurfaceProvider::mFrames") { MOZ_ASSERT(!mDecoder->IsMetadataDecode(), "Use MetadataDecodingTask for metadata decodes"); MOZ_ASSERT(!mDecoder->IsFirstFrameDecode(), "Use DecodedSurfaceProvider for single-frame image decodes"); // Calculate how many frames we need to decode in this animation before we // enter decode-on-demand mode. IntSize frameSize = aSurfaceKey.Size(); @@ -47,17 +42,16 @@ AnimationSurfaceProvider::AnimationSurfa mFrames.reset( new AnimationFrameRetainedBuffer(threshold, batch, aCurrentFrame)); } AnimationSurfaceProvider::~AnimationSurfaceProvider() { DropImageReference(); - mSharedAnimation->Destroy(); if (mDecoder) { mDecoder->SetFrameRecycler(nullptr); } } void AnimationSurfaceProvider::DropImageReference() { if (!mImage) { return; // Nothing to do. @@ -116,42 +110,25 @@ void AnimationSurfaceProvider::Reset() { if (restartDecoder) { DecodePool::Singleton()->AsyncRun(this); } } void AnimationSurfaceProvider::Advance(size_t aFrame) { bool restartDecoder; - RefPtr<SourceSurface> surface; - IntRect dirtyRect; { // Typical advancement of a frame. MutexAutoLock lock(mFramesMutex); restartDecoder = mFrames->AdvanceTo(aFrame); - - imgFrame* frame = mFrames->Get(aFrame, /* aForDisplay */ true); - MOZ_ASSERT(frame); - if (aFrame != 0) { - dirtyRect = frame->GetDirtyRect(); - } else { - MOZ_ASSERT(mFrames->SizeKnown()); - dirtyRect = mFrames->FirstFrameRefreshArea(); - } - surface = frame->GetSourceSurface(); - MOZ_ASSERT(surface); } if (restartDecoder) { DecodePool::Singleton()->AsyncRun(this); } - - mCompositedFrameRequested = false; - auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get()); - mSharedAnimation->SetCurrentFrame(sharedSurface, dirtyRect); } DrawableFrameRef AnimationSurfaceProvider::DrawableRef(size_t aFrame) { MutexAutoLock lock(mFramesMutex); if (Availability().IsPlaceholder()) { MOZ_ASSERT_UNREACHABLE("Calling DrawableRef() on a placeholder"); return DrawableFrameRef(); @@ -503,32 +480,10 @@ bool AnimationSurfaceProvider::ShouldPre RawAccessFrameRef AnimationSurfaceProvider::RecycleFrame( gfx::IntRect& aRecycleRect) { MutexAutoLock lock(mFramesMutex); MOZ_ASSERT(mFrames->IsRecycling()); return mFrames->RecycleFrame(aRecycleRect); } -nsresult AnimationSurfaceProvider::UpdateKey( - layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { - MOZ_ASSERT(NS_IsMainThread()); - - RefPtr<SourceSurface> surface; - { - MutexAutoLock lock(mFramesMutex); - imgFrame* frame = - mFrames->Get(mFrames->Displayed(), /* aForDisplay */ true); - if (!frame) { - return NS_ERROR_NOT_AVAILABLE; - } - - surface = frame->GetSourceSurface(); - } - - mCompositedFrameRequested = true; - auto* sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get()); - return mSharedAnimation->UpdateKey(sharedSurface, aManager, aResources, aKey); -} - } // namespace image } // namespace mozilla
--- a/image/AnimationSurfaceProvider.h +++ b/image/AnimationSurfaceProvider.h @@ -14,20 +14,16 @@ #include "Decoder.h" #include "FrameAnimator.h" #include "IDecodingTask.h" #include "ISurfaceProvider.h" #include "AnimationFrameBuffer.h" namespace mozilla { -namespace layers { -class SharedSurfacesAnimation; -} - namespace image { /** * An ISurfaceProvider that manages the decoding of animated images and * dynamically generates surfaces for the current playback state of the * animation. */ class AnimationSurfaceProvider final : public ISurfaceProvider, @@ -40,25 +36,29 @@ class AnimationSurfaceProvider final : p const SurfaceKey& aSurfaceKey, NotNull<Decoder*> aDecoder, size_t aCurrentFrame); ////////////////////////////////////////////////////////////////////////////// // ISurfaceProvider implementation. ////////////////////////////////////////////////////////////////////////////// public: + // We use the ISurfaceProvider constructor of DrawableSurface to indicate that + // our surfaces are computed lazily. + DrawableSurface Surface() override { + return DrawableSurface(WrapNotNull(this)); + } + bool IsFinished() const override; bool IsFullyDecoded() const override; size_t LogicalSizeInBytes() const override; void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) override; void Reset() override; void Advance(size_t aFrame) override; - bool MayAdvance() const override { return mCompositedFrameRequested; } - void MarkMayAdvance() override { mCompositedFrameRequested = true; } protected: DrawableFrameRef DrawableRef(size_t aFrame) override; already_AddRefed<imgFrame> GetFrame(size_t aFrame) override; // Animation frames are always locked. This is because we only want to release // their memory atomically (due to the surface cache discarding them). If they // were unlocked, the OS could end up releasing the memory of random frames @@ -81,25 +81,16 @@ class AnimationSurfaceProvider final : p ////////////////////////////////////////////////////////////////////////////// // IDecoderFrameRecycler implementation. ////////////////////////////////////////////////////////////////////////////// public: RawAccessFrameRef RecycleFrame(gfx::IntRect& aRecycleRect) override; - ////////////////////////////////////////////////////////////////////////////// - // IDecoderFrameRecycler implementation. - ////////////////////////////////////////////////////////////////////////////// - - public: - nsresult UpdateKey(layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, - wr::ImageKey& aKey) override; - private: virtual ~AnimationSurfaceProvider(); void DropImageReference(); void AnnounceSurfaceAvailable(); void FinishDecoding(); void RequestFrameDiscarding(); @@ -118,21 +109,14 @@ class AnimationSurfaceProvider final : p /// The decoder used to decode this animation. RefPtr<Decoder> mDecoder; /// A mutex to protect mFrames. Always taken after mDecodingMutex. mutable Mutex mFramesMutex; /// The frames of this animation, in order. UniquePtr<AnimationFrameBuffer> mFrames; - - /// Whether the current frame was requested for display since the last time we - /// advanced the animation. - bool mCompositedFrameRequested; - - /// - RefPtr<layers::SharedSurfacesAnimation> mSharedAnimation; }; } // namespace image } // namespace mozilla #endif // mozilla_image_AnimationSurfaceProvider_h
--- a/image/ClippedImage.cpp +++ b/image/ClippedImage.cpp @@ -293,31 +293,31 @@ ClippedImage::IsImageContainerAvailable( uint32_t aFlags) { if (!ShouldClip()) { return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags); } return false; } NS_IMETHODIMP_(ImgDrawResult) -ClippedImage::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { +ClippedImage::GetImageContainerAtSize(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aOutContainer) { // XXX(seth): We currently don't have a way of clipping the result of // GetImageContainer. We work around this by always returning null, but if it // ever turns out that ClippedImage is widely used on codepaths that can // actually benefit from GetImageContainer, it would be a good idea to fix // that method for performance reasons. if (!ShouldClip()) { - return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext, - aRegion, aFlags, aProvider); + return InnerImage()->GetImageContainerAtSize( + aRenderer, aSize, aSVGContext, aRegion, aFlags, aOutContainer); } return ImgDrawResult::NOT_SUPPORTED; } static bool MustCreateSurface(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, const uint32_t aFlags) {
--- a/image/ClippedImage.h +++ b/image/ClippedImage.h @@ -41,20 +41,20 @@ class ClippedImage : public ImageWrapper GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override; NS_IMETHOD_(already_AddRefed<SourceSurface>) GetFrameAtSize(const gfx::IntSize& aSize, uint32_t aWhichFrame, uint32_t aFlags) override; NS_IMETHOD_(bool) IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) override; NS_IMETHOD_(ImgDrawResult) - GetImageProvider(WindowRenderer* aRenderer, const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, - WebRenderImageProvider** aProvider) override; + GetImageContainerAtSize(WindowRenderer* aRenderer, const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, + layers::ImageContainer** aOutContainer) override; NS_IMETHOD_(ImgDrawResult) Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags, float aOpacity) override; NS_IMETHOD RequestDiscard() override; NS_IMETHOD_(Orientation) GetOrientation() override; NS_IMETHOD_(nsIntRect)
--- a/image/DecodedSurfaceProvider.cpp +++ b/image/DecodedSurfaceProvider.cpp @@ -1,23 +1,21 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "DecodedSurfaceProvider.h" #include "mozilla/StaticPrefs_image.h" -#include "mozilla/layers/SharedSurfacesChild.h" #include "nsProxyRelease.h" #include "Decoder.h" using namespace mozilla::gfx; -using namespace mozilla::layers; namespace mozilla { namespace image { DecodedSurfaceProvider::DecodedSurfaceProvider(NotNull<RasterImage*> aImage, const SurfaceKey& aSurfaceKey, NotNull<Decoder*> aDecoder) : ISurfaceProvider(ImageKey(aImage.get()), aSurfaceKey, @@ -202,33 +200,10 @@ void DecodedSurfaceProvider::FinishDecod DropImageReference(); } bool DecodedSurfaceProvider::ShouldPreferSyncRun() const { return mDecoder->ShouldSyncDecode( StaticPrefs::image_mem_decode_bytes_at_a_time_AtStartup()); } -nsresult DecodedSurfaceProvider::UpdateKey( - layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { - MOZ_ASSERT(mSurface); - RefPtr<SourceSurface> surface = mSurface->GetSourceSurface(); - if (!surface) { - return NS_ERROR_FAILURE; - } - - return SharedSurfacesChild::Share(surface, aManager, aResources, aKey); -} - -nsresult SimpleSurfaceProvider::UpdateKey( - layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { - RefPtr<SourceSurface> surface = mSurface->GetSourceSurface(); - if (!surface) { - return NS_ERROR_FAILURE; - } - - return SharedSurfacesChild::Share(surface, aManager, aResources, aKey); -} - } // namespace image } // namespace mozilla
--- a/image/DecodedSurfaceProvider.h +++ b/image/DecodedSurfaceProvider.h @@ -50,25 +50,16 @@ class DecodedSurfaceProvider final : pub public: void Run() override; bool ShouldPreferSyncRun() const override; // Full decodes are low priority compared to metadata decodes because they // don't block layout or page load. TaskPriority Priority() const override { return TaskPriority::eLow; } - ////////////////////////////////////////////////////////////////////////////// - // WebRenderImageProvider implementation. - ////////////////////////////////////////////////////////////////////////////// - - public: - nsresult UpdateKey(layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, - wr::ImageKey& aKey) override; - private: virtual ~DecodedSurfaceProvider(); void DropImageReference(); void CheckForNewSurface(); void FinishDecoding(); /// The image associated with our decoder. Dropped after decoding.
--- a/image/DynamicImage.cpp +++ b/image/DynamicImage.cpp @@ -118,17 +118,17 @@ DynamicImage::GetResolution() { return { NS_IMETHODIMP DynamicImage::GetType(uint16_t* aType) { *aType = imgIContainer::TYPE_RASTER; return NS_OK; } NS_IMETHODIMP -DynamicImage::GetProviderId(uint32_t* aId) { +DynamicImage::GetProducerId(uint32_t* aId) { *aId = 0; return NS_OK; } NS_IMETHODIMP DynamicImage::GetAnimated(bool* aAnimated) { *aAnimated = false; return NS_OK; @@ -165,22 +165,22 @@ DynamicImage::WillDrawOpaqueNow() { retu NS_IMETHODIMP_(bool) DynamicImage::IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) { return false; } NS_IMETHODIMP_(ImgDrawResult) -DynamicImage::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { +DynamicImage::GetImageContainerAtSize(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aContainer) { return ImgDrawResult::NOT_SUPPORTED; } NS_IMETHODIMP_(ImgDrawResult) DynamicImage::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, SamplingFilter aSamplingFilter, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags,
--- a/image/FrameAnimator.cpp +++ b/image/FrameAnimator.cpp @@ -313,16 +313,17 @@ RefreshResult FrameAnimator::AdvanceFram MOZ_ASSERT(loops <= CheckedUint64(aState.mLoopRemainingCount).value()); aState.mLoopRemainingCount -= CheckedInt32(loops).value(); } } } // Set currentAnimationFrameIndex at the last possible moment aState.mCurrentAnimationFrameIndex = nextFrameIndex; + aState.mCompositedFrameRequested = false; aCurrentFrame = std::move(nextFrame); aFrames.Advance(nextFrameIndex); // If we're here, we successfully advanced the frame. ret.mFrameAdvanced = true; return ret; } @@ -389,17 +390,17 @@ RefreshResult FrameAnimator::RequestRefr } TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime(aState, currentFrame->GetTimeout()); // If nothing has accessed the composited frame since the last time we // advanced, then there is no point in continuing to advance the animation. // This has the effect of freezing the animation while not in view. - if (!result.Surface().MayAdvance() && + if (!aState.mCompositedFrameRequested && aState.MaybeAdvanceAnimationFrameTime(aTime)) { return ret; } while (currentFrameEndTime <= aTime) { TimeStamp oldFrameEndTime = currentFrameEndTime; RefreshResult frameRes = @@ -437,28 +438,23 @@ RefreshResult FrameAnimator::RequestRefr MOZ_ASSERT(!aState.mIsCurrentlyDecoded || !aState.mCompositedFrameInvalid); return ret; } LookupResult FrameAnimator::GetCompositedFrame(AnimationState& aState, bool aMarkUsed) { + aState.mCompositedFrameRequested = true; + LookupResult result = SurfaceCache::Lookup( ImageKey(mImage), RasterSurfaceKey(mSize, DefaultSurfaceFlags(), PlaybackType::eAnimated), aMarkUsed); - if (result) { - // If we are getting the frame directly (e.g. through tests or canvas), we - // need to ensure the animation is marked to allow advancing to the next - // frame. - result.Surface().MarkMayAdvance(); - } - if (aState.mCompositedFrameInvalid) { MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup()); MOZ_ASSERT(aState.GetHasRequestedDecode()); MOZ_ASSERT(!aState.GetIsCurrentlyDecoded()); if (result.Type() == MatchType::EXACT) { // If our composited frame is marked as invalid but our frames are in the // surface cache we might just have not updated our internal state yet.
--- a/image/FrameAnimator.h +++ b/image/FrameAnimator.h @@ -30,16 +30,17 @@ class AnimationState { mLoopRemainingCount(-1), mLoopCount(-1), mFirstFrameTimeout(FrameTimeout::FromRawMilliseconds(0)), mAnimationMode(aAnimationMode), mHasBeenDecoded(false), mHasRequestedDecode(false), mIsCurrentlyDecoded(false), mCompositedFrameInvalid(false), + mCompositedFrameRequested(false), mDiscarded(false) {} /** * Call this whenever a decode completes, a decode starts, or the image is * discarded. It will update the internal state. Specifically mDiscarded, * mCompositedFrameInvalid, and mIsCurrentlyDecoded. If aAllowInvalidation * is true then returns a rect to invalidate. */ @@ -234,16 +235,20 @@ class AnimationState { //! Whether this image is currently fully decoded. bool mIsCurrentlyDecoded; //! Whether the composited frame is valid to draw to the screen, note that //! the composited frame can exist and be filled with image data but not //! valid to draw to the screen. bool mCompositedFrameInvalid; + //! Whether the composited frame was requested from the animator since the + //! last time we advanced the animation. + bool mCompositedFrameRequested; + //! Whether this image is currently discarded. Only set to true after the //! image has been decoded at least once. bool mDiscarded; }; /** * RefreshResult is used to let callers know how the state of the animation * changed during a call to FrameAnimator::RequestRefresh().
--- a/image/FrozenImage.cpp +++ b/image/FrozenImage.cpp @@ -55,25 +55,25 @@ FrozenImage::IsImageContainerAvailable(W uint32_t aFlags) { if (IsNonAnimated()) { return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags); } return false; } NS_IMETHODIMP_(ImgDrawResult) -FrozenImage::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { +FrozenImage::GetImageContainerAtSize(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aOutContainer) { if (IsNonAnimated()) { - return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext, - aRegion, aFlags, aProvider); + return InnerImage()->GetImageContainerAtSize( + aRenderer, aSize, aSVGContext, aRegion, aFlags, aOutContainer); } // XXX(seth): GetImageContainer does not currently support anything but the // current frame. We work around this by always returning null, but if it ever // turns out that FrozenImage is widely used on codepaths that can actually // benefit from GetImageContainer, it would be a good idea to fix that method // for performance reasons. return ImgDrawResult::NOT_SUPPORTED;
--- a/image/FrozenImage.h +++ b/image/FrozenImage.h @@ -40,20 +40,20 @@ class FrozenImage : public ImageWrapper GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override; NS_IMETHOD_(already_AddRefed<SourceSurface>) GetFrameAtSize(const gfx::IntSize& aSize, uint32_t aWhichFrame, uint32_t aFlags) override; NS_IMETHOD_(bool) IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) override; NS_IMETHOD_(ImgDrawResult) - GetImageProvider(WindowRenderer* aRenderer, const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, - WebRenderImageProvider** aProvider) override; + GetImageContainerAtSize(WindowRenderer* aRenderer, const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, + layers::ImageContainer** aOutContainer) override; NS_IMETHOD_(ImgDrawResult) Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags, float aOpacity) override; NS_IMETHOD StartDecoding(uint32_t aFlags, uint32_t aWhichFrame) override; NS_IMETHOD_(bool) StartDecodingWithResult(uint32_t aFlags, uint32_t aWhichFrame) override;
--- a/image/ISurfaceProvider.h +++ b/image/ISurfaceProvider.h @@ -12,47 +12,46 @@ #define mozilla_image_ISurfaceProvider_h #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/NotNull.h" #include "mozilla/TimeStamp.h" #include "mozilla/gfx/2D.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "imgFrame.h" #include "SurfaceCache.h" namespace mozilla { namespace image { class CachedSurface; class DrawableSurface; /** * An interface for objects which can either store a surface or dynamically * generate one. */ -class ISurfaceProvider : public WebRenderImageProvider { +class ISurfaceProvider { public: // Subclasses may or may not be XPCOM classes, so we just require that they // implement AddRef and Release. NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING /// @return key data used for identifying which image this ISurfaceProvider is /// associated with in the surface cache. ImageKey GetImageKey() const { return mImageKey; } /// @return key data used to uniquely identify this ISurfaceProvider's cache /// entry in the surface cache. const SurfaceKey& GetSurfaceKey() const { return mSurfaceKey; } - /// @return a drawable reference to a surface. - DrawableSurface Surface(); + /// @return a (potentially lazily computed) drawable reference to a surface. + virtual DrawableSurface Surface(); /// @return true if DrawableRef() will return a completely decoded surface. virtual bool IsFinished() const = 0; /// @return true if the underlying decoder is currently fully decoded. For /// animated images, this means that at least every frame has been decoded /// at least once. It does not guarantee that all of the frames are present, /// as the surface provider has the option to discard as it deems necessary. @@ -77,31 +76,28 @@ class ISurfaceProvider : public WebRende return; } ref->AddSizeOfExcludingThis(aMallocSizeOf, aCallback); } virtual void Reset() {} virtual void Advance(size_t aFrame) {} - virtual bool MayAdvance() const { return false; } - virtual void MarkMayAdvance() {} /// @return the availability state of this ISurfaceProvider, which indicates /// whether DrawableRef() could successfully return a surface. Should only be /// called from SurfaceCache code as it relies on SurfaceCache for /// synchronization. AvailabilityState& Availability() { return mAvailability; } const AvailabilityState& Availability() const { return mAvailability; } protected: ISurfaceProvider(const ImageKey aImageKey, const SurfaceKey& aSurfaceKey, AvailabilityState aAvailability) - : WebRenderImageProvider(aImageKey), - mImageKey(aImageKey), + : mImageKey(aImageKey), mSurfaceKey(aSurfaceKey), mAvailability(aAvailability) { MOZ_ASSERT(aImageKey, "Must have a valid image key"); } virtual ~ISurfaceProvider() {} /// @return an eagerly computed drawable reference to a surface. For @@ -144,16 +140,20 @@ class ISurfaceProvider : public WebRende * Note that the surface may be computed lazily, so a DrawableSurface should not * be dereferenced (i.e., operator->() should not be called) until you're * sure that you want to draw it. */ class MOZ_STACK_CLASS DrawableSurface final { public: DrawableSurface() : mHaveSurface(false) {} + explicit DrawableSurface(DrawableFrameRef&& aDrawableRef) + : mDrawableRef(std::move(aDrawableRef)), + mHaveSurface(bool(mDrawableRef)) {} + explicit DrawableSurface(NotNull<ISurfaceProvider*> aProvider) : mProvider(aProvider), mHaveSurface(true) {} DrawableSurface(DrawableSurface&& aOther) : mDrawableRef(std::move(aOther.mDrawableRef)), mProvider(std::move(aOther.mProvider)), mHaveSurface(aOther.mHaveSurface) { aOther.mHaveSurface = false; @@ -217,48 +217,26 @@ class MOZ_STACK_CLASS DrawableSurface fi if (!mProvider) { MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); return; } mProvider->Advance(aFrame); } - bool MayAdvance() const { - if (!mProvider) { - MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); - return false; - } - - return mProvider->MayAdvance(); - } - - void MarkMayAdvance() { - if (!mProvider) { - MOZ_ASSERT_UNREACHABLE("Trying to advance a static DrawableSurface?"); - return; - } - - mProvider->MarkMayAdvance(); - } - bool IsFullyDecoded() const { if (!mProvider) { MOZ_ASSERT_UNREACHABLE( "Trying to check decoding state of a static DrawableSurface?"); return false; } return mProvider->IsFullyDecoded(); } - void TakeProvider(WebRenderImageProvider** aOutProvider) { - mProvider.forget(aOutProvider); - } - explicit operator bool() const { return mHaveSurface; } imgFrame* operator->() { return DrawableRef().get(); } private: DrawableSurface(const DrawableSurface& aOther) = delete; DrawableSurface& operator=(const DrawableSurface& aOther) = delete; DrawableFrameRef& DrawableRef() { @@ -278,19 +256,20 @@ class MOZ_STACK_CLASS DrawableSurface fi } DrawableFrameRef mDrawableRef; RefPtr<ISurfaceProvider> mProvider; bool mHaveSurface; }; // Surface() is implemented here so that DrawableSurface's definition is -// visible. +// visible. This default implementation eagerly obtains a DrawableFrameRef for +// the first frame and is intended for static ISurfaceProviders. inline DrawableSurface ISurfaceProvider::Surface() { - return DrawableSurface(WrapNotNull(this)); + return DrawableSurface(DrawableRef(/* aFrame = */ 0)); } /** * An ISurfaceProvider that stores a single surface. */ class SimpleSurfaceProvider final : public ISurfaceProvider { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SimpleSurfaceProvider, override) @@ -305,20 +284,16 @@ class SimpleSurfaceProvider final : publ bool IsFinished() const override { return mSurface->IsFinished(); } size_t LogicalSizeInBytes() const override { gfx::IntSize size = mSurface->GetSize(); return size.width * size.height * mSurface->GetBytesPerPixel(); } - nsresult UpdateKey(layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, - wr::ImageKey& aKey) override; - protected: DrawableFrameRef DrawableRef(size_t aFrame) override { MOZ_ASSERT(aFrame == 0, "Requesting an animation frame from a SimpleSurfaceProvider?"); return mSurface->DrawableRef(); } bool IsLocked() const override { return bool(mLockRef); }
--- a/image/Image.cpp +++ b/image/Image.cpp @@ -2,42 +2,32 @@ /* 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 "Image.h" #include "imgRequest.h" #include "Layers.h" // for LayerManager -#include "WebRenderImageProvider.h" #include "nsIObserverService.h" #include "nsRefreshDriver.h" #include "nsContentUtils.h" -#include "mozilla/Atomics.h" #include "mozilla/gfx/Point.h" #include "mozilla/gfx/Rect.h" #include "mozilla/gfx/SourceSurfaceRawData.h" #include "mozilla/Services.h" #include "mozilla/SizeOfState.h" #include "mozilla/TimeStamp.h" #include "mozilla/Tuple.h" // for Tie #include "mozilla/layers/SharedSurfacesChild.h" +#include "SourceSurfaceBlobImage.h" namespace mozilla { namespace image { -WebRenderImageProvider::WebRenderImageProvider(const ImageResource* aImage) - : mProviderId(aImage->GetImageProviderId()) {} - -/* static */ ImageProviderId WebRenderImageProvider::AllocateProviderId() { - // Callable on all threads. - static Atomic<ImageProviderId> sProviderId(0u); - return ++sProviderId; -} - /////////////////////////////////////////////////////////////////////////////// // Memory Reporting /////////////////////////////////////////////////////////////////////////////// ImageMemoryCounter::ImageMemoryCounter(imgRequest* aRequest, SizeOfState& aState, bool aIsUsed) : mProgress(UINT32_MAX), mType(UINT16_MAX), @@ -115,32 +105,352 @@ bool ImageResource::GetSpecTruncatedTo1k if (sMaxTruncatedLength >= aSpec.Length()) { return true; } aSpec.Truncate(sMaxTruncatedLength); return false; } +void ImageResource::SetCurrentImage(layers::ImageContainer* aContainer, + gfx::SourceSurface* aSurface, + const Maybe<gfx::IntRect>& aDirtyRect) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aContainer); + + if (!aSurface) { + // The OS threw out some or all of our buffer. We'll need to wait for the + // redecode (which was automatically triggered by GetFrame) to complete. + return; + } + + // |image| holds a reference to a SourceSurface which in turn holds a lock on + // the current frame's data buffer, ensuring that it doesn't get freed as + // long as the layer system keeps this ImageContainer alive. + RefPtr<layers::Image> image = new layers::SourceSurfaceImage(aSurface); + + // We can share the producer ID with other containers because it is only + // used internally to validate the frames given to a particular container + // so that another object cannot add its own. Similarly the frame ID is + // only used internally to ensure it is always increasing, and skipping + // IDs from an individual container's perspective is acceptable. + AutoTArray<layers::ImageContainer::NonOwningImage, 1> imageList; + imageList.AppendElement(layers::ImageContainer::NonOwningImage( + image, TimeStamp(), mLastFrameID++, mImageProducerID)); + + if (aDirtyRect) { + aContainer->SetCurrentImagesInTransaction(imageList); + } else { + aContainer->SetCurrentImages(imageList); + } + + // If we are animated, then we should request that the image container be + // treated as such, to avoid display list rebuilding to update frames for + // WebRender. + if (mProgressTracker->GetProgress() & FLAG_IS_ANIMATED) { + if (aDirtyRect) { + layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface, + aDirtyRect.ref()); + } else { + gfx::IntRect dirtyRect(gfx::IntPoint(0, 0), aSurface->GetSize()); + layers::SharedSurfacesChild::UpdateAnimation(aContainer, aSurface, + dirtyRect); + } + } +} + +ImgDrawResult ImageResource::GetImageContainerImpl( + WindowRenderer* aRenderer, const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, + layers::ImageContainer** aOutContainer) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aRenderer); + MOZ_ASSERT((aFlags & + ~(FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST | FLAG_RECORD_BLOB | + FLAG_ASYNC_NOTIFY | FLAG_HIGH_QUALITY_SCALING)) == FLAG_NONE, + "Unsupported flag passed to GetImageContainer"); + + ImgDrawResult drawResult; + gfx::IntSize size; + Tie(drawResult, size) = GetImageContainerSize(aRenderer, aSize, aFlags); + if (drawResult != ImgDrawResult::SUCCESS) { + return drawResult; + } + + MOZ_ASSERT(!size.IsEmpty()); + + if (mAnimationConsumers == 0) { + SendOnUnlockedDraw(aFlags); + } + + uint32_t flags = (aFlags & ~(FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST)) | + FLAG_ASYNC_NOTIFY; + RefPtr<layers::ImageContainer> container; + ImageContainerEntry* entry = nullptr; + int i = mImageContainers.Length() - 1; + for (; i >= 0; --i) { + entry = &mImageContainers[i]; + if (size == entry->mSize && flags == entry->mFlags && + aSVGContext == entry->mSVGContext && aRegion == entry->mRegion) { + // Lack of a container is handled below. + container = RefPtr<layers::ImageContainer>(entry->mContainer); + break; + } else if (entry->mContainer.IsDead()) { + // Stop tracking if our weak pointer to the image container was freed. + mImageContainers.RemoveElementAt(i); + } + } + + if (container) { + switch (entry->mLastDrawResult) { + case ImgDrawResult::SUCCESS: + case ImgDrawResult::BAD_IMAGE: + case ImgDrawResult::BAD_ARGS: + case ImgDrawResult::NOT_SUPPORTED: + container.forget(aOutContainer); + return entry->mLastDrawResult; + case ImgDrawResult::NOT_READY: + case ImgDrawResult::INCOMPLETE: + case ImgDrawResult::TEMPORARY_ERROR: + // Temporary conditions where we need to rerequest the frame to recover. + break; + case ImgDrawResult::WRONG_SIZE: + // Unused by GetFrameInternal + default: + MOZ_ASSERT_UNREACHABLE("Unhandled ImgDrawResult type!"); + container.forget(aOutContainer); + return entry->mLastDrawResult; + } + } + + AutoProfilerImagePaintMarker PROFILER_RAII(this); +#ifdef DEBUG + NotifyDrawingObservers(); +#endif + + gfx::IntSize bestSize; + RefPtr<gfx::SourceSurface> surface; + Tie(drawResult, bestSize, surface) = GetFrameInternal( + size, aSVGContext, aRegion, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY); + + // The requested size might be refused by the surface cache (i.e. due to + // factor-of-2 mode). In that case we don't want to create an entry for this + // specific size, but rather re-use the entry for the substituted size. + if (bestSize != size) { + MOZ_ASSERT(!bestSize.IsEmpty()); + + // We can only remove the entry if we no longer have a container, because if + // there are strong references to it remaining, we need to still update it + // in UpdateImageContainer. + if (i >= 0 && !container) { + mImageContainers.RemoveElementAt(i); + } + + // Forget about the stale container, if any. This lets the entry creation + // logic do its job below, if it turns out there is no existing best entry + // or the best entry doesn't have a container. + container = nullptr; + + // We need to do the entry search again for the new size. We skip pruning + // because we did this above once already, but ImageContainer is threadsafe, + // so there is a remote possibility it got freed. + i = mImageContainers.Length() - 1; + for (; i >= 0; --i) { + entry = &mImageContainers[i]; + if (bestSize == entry->mSize && flags == entry->mFlags && + aSVGContext == entry->mSVGContext && aRegion == entry->mRegion) { + container = RefPtr<layers::ImageContainer>(entry->mContainer); + if (container) { + switch (entry->mLastDrawResult) { + case ImgDrawResult::SUCCESS: + case ImgDrawResult::BAD_IMAGE: + case ImgDrawResult::BAD_ARGS: + case ImgDrawResult::NOT_SUPPORTED: + container.forget(aOutContainer); + return entry->mLastDrawResult; + case ImgDrawResult::NOT_READY: + case ImgDrawResult::INCOMPLETE: + case ImgDrawResult::TEMPORARY_ERROR: + // Temporary conditions where we need to rerequest the frame to + // recover. We have already done so! + break; + case ImgDrawResult::WRONG_SIZE: + // Unused by GetFrameInternal + default: + MOZ_ASSERT_UNREACHABLE("Unhandled DrawResult type!"); + container.forget(aOutContainer); + return entry->mLastDrawResult; + } + } + break; + } + } + } + + if (!container) { + // We need a new ImageContainer, so create one. + container = MakeAndAddRef<layers::ImageContainer>(); + + if (i >= 0) { + entry->mContainer = container; + } else { + entry = mImageContainers.AppendElement(ImageContainerEntry( + bestSize, aSVGContext, aRegion, container.get(), flags)); + } + } + + SetCurrentImage(container, surface, Nothing()); + entry->mLastDrawResult = drawResult; + container.forget(aOutContainer); + return drawResult; +} + +bool ImageResource::UpdateImageContainer( + const Maybe<gfx::IntRect>& aDirtyRect) { + MOZ_ASSERT(NS_IsMainThread()); + + for (int i = mImageContainers.Length() - 1; i >= 0; --i) { + ImageContainerEntry& entry = mImageContainers[i]; + RefPtr<layers::ImageContainer> container(entry.mContainer); + if (container) { + // Blob recordings should just be marked as dirty. We will regenerate the + // recording when the display list update comes around. + if (entry.mFlags & FLAG_RECORD_BLOB) { + AutoTArray<layers::ImageContainer::OwningImage, 1> images; + container->GetCurrentImages(&images); + if (images.IsEmpty()) { + MOZ_ASSERT_UNREACHABLE("Empty container!"); + continue; + } + + RefPtr<gfx::SourceSurface> surface = + images[0].mImage->GetAsSourceSurface(); + if (!surface || surface->GetType() != gfx::SurfaceType::BLOB_IMAGE) { + MOZ_ASSERT_UNREACHABLE("No/wrong surface in container!"); + continue; + } + + static_cast<SourceSurfaceBlobImage*>(surface.get())->MarkDirty(); + continue; + } + + gfx::IntSize bestSize; + RefPtr<gfx::SourceSurface> surface; + Tie(entry.mLastDrawResult, bestSize, surface) = + GetFrameInternal(entry.mSize, entry.mSVGContext, entry.mRegion, + FRAME_CURRENT, entry.mFlags); + + // It is possible that this is a factor-of-2 substitution. Since we + // managed to convert the weak reference into a strong reference, that + // means that an imagelib user still is holding onto the container. thus + // we cannot consolidate and must keep updating the duplicate container. + if (aDirtyRect) { + SetCurrentImage(container, surface, aDirtyRect); + } else { + gfx::IntRect dirtyRect(gfx::IntPoint(0, 0), bestSize); + SetCurrentImage(container, surface, Some(dirtyRect)); + } + } else { + // Stop tracking if our weak pointer to the image container was freed. + mImageContainers.RemoveElementAt(i); + } + } + + return !mImageContainers.IsEmpty(); +} + void ImageResource::CollectSizeOfSurfaces( nsTArray<SurfaceMemoryCounter>& aCounters, MallocSizeOf aMallocSizeOf) const { - SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf); + MOZ_ASSERT(NS_IsMainThread()); + + for (const auto& entry : mImageContainers) { + RefPtr<layers::ImageContainer> container(entry.mContainer); + if (!container) { + continue; + } + + AutoTArray<layers::ImageContainer::OwningImage, 1> images; + container->GetCurrentImages(&images); + if (images.IsEmpty()) { + continue; + } + + RefPtr<gfx::SourceSurface> surface = images[0].mImage->GetAsSourceSurface(); + if (!surface) { + MOZ_ASSERT_UNREACHABLE("No surface in container!"); + continue; + } + + // The surface might be wrapping another. + bool isMappedSurface = surface->GetType() == gfx::SurfaceType::DATA_MAPPED; + const gfx::SourceSurface* actualSurface = + isMappedSurface + ? static_cast<gfx::SourceSurfaceMappedData*>(surface.get()) + ->GetScopedSurface() + : surface.get(); + + // Check if the surface is already in the report. Ignore if so. + bool found = false; + for (const auto& counter : aCounters) { + if (counter.Surface() == actualSurface) { + found = true; + break; + } + } + if (found) { + continue; + } + + // The surface isn't in the report, so it isn't stored in SurfaceCache. We + // need to add our own entry here so that it will be included in the memory + // report. + gfx::SourceSurface::SizeOfInfo info; + surface->SizeOfExcludingThis(aMallocSizeOf, info); + + uint32_t heapBytes = aMallocSizeOf(actualSurface); + if (isMappedSurface) { + heapBytes += aMallocSizeOf(surface.get()); + } + + SurfaceKey key = ContainerSurfaceKey(surface->GetSize(), entry.mSVGContext, + ToSurfaceFlags(entry.mFlags)); + SurfaceMemoryCounter counter(key, actualSurface, /* aIsLocked */ false, + /* aCannotSubstitute */ false, + /* aIsFactor2 */ false, /* aFinished */ true, + SurfaceMemoryCounterType::CONTAINER); + + counter.Values().SetDecodedHeap(info.mHeapBytes + heapBytes); + counter.Values().SetDecodedNonHeap(info.mNonHeapBytes); + counter.Values().SetDecodedUnknown(info.mUnknownBytes); + counter.Values().SetExternalHandles(info.mExternalHandles); + counter.Values().SetExternalId(info.mExternalId); + counter.Values().SetSurfaceTypes(info.mTypes); + + aCounters.AppendElement(counter); + } +} + +void ImageResource::ReleaseImageContainer() { + MOZ_ASSERT(NS_IsMainThread()); + mImageContainers.Clear(); } // Constructor ImageResource::ImageResource(nsIURI* aURI) : mURI(aURI), mInnerWindowId(0), mAnimationConsumers(0), mAnimationMode(kNormalAnimMode), mInitialized(false), mAnimating(false), mError(false), - mProviderId(WebRenderImageProvider::AllocateProviderId()) {} + mImageProducerID(layers::ImageContainer::AllocateProducerID()), + mLastFrameID(0) {} ImageResource::~ImageResource() { // Ask our ProgressTracker to drop its weak reference to us. mProgressTracker->ResetImage(); } void ImageResource::IncrementAnimationConsumers() { MOZ_ASSERT(NS_IsMainThread(),
--- a/image/Image.h +++ b/image/Image.h @@ -17,17 +17,16 @@ #include "gfx2DGlue.h" #include "imgIContainer.h" #include "ImageContainer.h" #include "ImageRegion.h" #include "LookupResult.h" #include "nsStringFwd.h" #include "ProgressTracker.h" #include "SurfaceCache.h" -#include "WebRenderImageProvider.h" class imgRequest; class nsIRequest; class nsIInputStream; namespace mozilla { namespace image { @@ -85,37 +84,40 @@ struct MemoryCounter { uint64_t mExternalId; uint32_t mSurfaceTypes; }; enum class SurfaceMemoryCounterType { NORMAL, CONTAINER }; struct SurfaceMemoryCounter { SurfaceMemoryCounter( - const SurfaceKey& aKey, bool aIsLocked, bool aCannotSubstitute, - bool aIsFactor2, bool aFinished, + const SurfaceKey& aKey, const gfx::SourceSurface* aSurface, + bool aIsLocked, bool aCannotSubstitute, bool aIsFactor2, bool aFinished, SurfaceMemoryCounterType aType = SurfaceMemoryCounterType::NORMAL) : mKey(aKey), + mSurface(aSurface), mType(aType), mIsLocked(aIsLocked), mCannotSubstitute(aCannotSubstitute), mIsFactor2(aIsFactor2), mFinished(aFinished) {} const SurfaceKey& Key() const { return mKey; } + const gfx::SourceSurface* Surface() const { return mSurface; } MemoryCounter& Values() { return mValues; } const MemoryCounter& Values() const { return mValues; } SurfaceMemoryCounterType Type() const { return mType; } bool IsLocked() const { return mIsLocked; } bool CannotSubstitute() const { return mCannotSubstitute; } bool IsFactor2() const { return mIsFactor2; } bool IsFinished() const { return mFinished; } private: const SurfaceKey mKey; + const gfx::SourceSurface* MOZ_NON_OWNING_REF mSurface; MemoryCounter mValues; const SurfaceMemoryCounterType mType; const bool mIsLocked; const bool mCannotSubstitute; const bool mIsFactor2; const bool mFinished; }; @@ -324,22 +326,24 @@ class ImageResource : public Image { /* * Should be called by its subclasses after they populate @aCounters so that * we can cross reference against any of our ImageContainers that contain * surfaces not in the cache. */ void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters, MallocSizeOf aMallocSizeOf) const override; - ImageProviderId GetImageProviderId() const { return mProviderId; } - protected: explicit ImageResource(nsIURI* aURI); ~ImageResource(); + layers::ContainerProducerID GetImageProducerId() const { + return mImageProducerID; + } + bool GetSpecTruncatedTo1k(nsCString& aSpec) const; // Shared functionality for implementors of imgIContainer. Every // implementation of attribute animationMode should forward here. nsresult GetAnimationModeInternal(uint16_t* aAnimationMode); nsresult SetAnimationModeInternal(uint16_t aAnimationMode); /** @@ -383,16 +387,70 @@ class ImageResource : public Image { TimeStamp mLastRefreshTime; uint64_t mInnerWindowId; uint32_t mAnimationConsumers; uint16_t mAnimationMode; // Enum values in imgIContainer bool mInitialized : 1; // Have we been initialized? bool mAnimating : 1; // Are we currently animating? bool mError : 1; // Error handling + /** + * Attempt to find a matching cached surface in the SurfaceCache, and if not + * available, request the production of such a surface (either synchronously + * or asynchronously). + * + * If the draw result is BAD_IMAGE, BAD_ARGS or NOT_READY, the size will be + * the same as aSize. If it is TEMPORARY_ERROR, INCOMPLETE, or SUCCESS, the + * size is a hint as to what we expect the surface size to be, once the best + * fitting size is available. It may or may not match the size of the surface + * returned at this moment. This is useful for choosing how to store the final + * result (e.g. if going into an ImageContainer, ideally we would share the + * same container for many requested sizes, if they all end up with the same + * best fit size in the end). + * + * A valid surface should only be returned for SUCCESS and INCOMPLETE. + * + * Any other draw result is invalid. + */ + virtual Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>> + GetFrameInternal(const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aWhichFrame, + uint32_t aFlags) { + return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, + RefPtr<gfx::SourceSurface>()); + } + + /** + * Calculate the estimated size to use for an image container with the given + * parameters. It may not be the same as the given size, and it may not be + * the same as the size of the surface in the image container, but it is the + * best effort estimate. + */ + virtual Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize( + WindowRenderer* aRenderer, const gfx::IntSize& aSize, uint32_t aFlags) { + return MakeTuple(ImgDrawResult::NOT_SUPPORTED, gfx::IntSize(0, 0)); + } + + ImgDrawResult GetImageContainerImpl(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aContainer); + + /** + * Re-requests the appropriate frames for each image container using + * GetFrameInternal. + * @returns True if any image containers were updated, else false. + */ + bool UpdateImageContainer(const Maybe<gfx::IntRect>& aDirtyRect); + + void ReleaseImageContainer(); + class MOZ_RAII AutoProfilerImagePaintMarker { public: explicit AutoProfilerImagePaintMarker(ImageResource* self) : mStartTime(TimeStamp::Now()) { nsAutoCString spec; if (self->mURI && profiler_thread_is_being_profiled()) { static const size_t sMaxTruncatedLength = 1024; self->mURI->GetSpec(mSpec); @@ -411,15 +469,47 @@ class ImageResource : public Image { } protected: TimeStamp mStartTime; nsAutoCString mSpec; }; private: - ImageProviderId mProviderId; + void SetCurrentImage(layers::ImageContainer* aContainer, + gfx::SourceSurface* aSurface, + const Maybe<gfx::IntRect>& aDirtyRect); + + struct ImageContainerEntry { + ImageContainerEntry(const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + layers::ImageContainer* aContainer, uint32_t aFlags) + : mSize(aSize), + mSVGContext(aSVGContext), + mRegion(aRegion), + mContainer(aContainer), + mLastDrawResult(ImgDrawResult::NOT_READY), + mFlags(aFlags) {} + + gfx::IntSize mSize; + Maybe<SVGImageContext> mSVGContext; + Maybe<ImageIntRegion> mRegion; + // A weak pointer to our ImageContainer, which stays alive only as long as + // the layer system needs it. + ThreadSafeWeakPtr<layers::ImageContainer> mContainer; + // If mContainer is non-null, this contains the ImgDrawResult we obtained + // the last time we updated it. + ImgDrawResult mLastDrawResult; + // Cached flags to use for decoding. FLAG_ASYNC_NOTIFY should always be set + // but FLAG_HIGH_QUALITY_SCALING may vary. + uint32_t mFlags; + }; + + AutoTArray<ImageContainerEntry, 1> mImageContainers; + layers::ImageContainer::ProducerID mImageProducerID; + layers::ImageContainer::FrameID mLastFrameID; }; } // namespace image } // namespace mozilla #endif // mozilla_image_Image_h
--- a/image/ImageRegion.h +++ b/image/ImageRegion.h @@ -8,17 +8,16 @@ #include "gfxMatrix.h" #include "gfxPoint.h" #include "gfxRect.h" #include "gfxTypes.h" #include "mozilla/gfx/Matrix.h" #include "mozilla/gfx/Types.h" #include "nsSize.h" -#include "PLDHashTable.h" // for PLDHashNumber namespace mozilla { namespace image { /** * An axis-aligned rectangle in tiled image space, with an optional sampling * restriction rect. The drawing code ensures that if a sampling restriction * rect is present, any pixels sampled during the drawing process are found @@ -235,22 +234,16 @@ class ImageIntRegion { bool operator==(const ImageIntRegion& aOther) const { return mExtendMode == aOther.mExtendMode && mIsRestricted == aOther.mIsRestricted && mRect.IsEqualEdges(aOther.mRect) && (!mIsRestricted || mRestriction.IsEqualEdges(aOther.mRestriction)); } - PLDHashNumber Hash() const { - return HashGeneric(mRect.x, mRect.y, mRect.width, mRect.height, - mRestriction.x, mRestriction.y, mRestriction.width, - mRestriction.height, mExtendMode, mIsRestricted); - } - /* ImageIntRegion() : mIsRestricted(false) { } */ private: explicit ImageIntRegion(const mozilla::gfx::IntRect& aRect, ExtendMode aExtendMode) : mRect(aRect), mExtendMode(aExtendMode), mIsRestricted(false) {} ImageIntRegion(const mozilla::gfx::IntRect& aRect,
--- a/image/ImageWrapper.cpp +++ b/image/ImageWrapper.cpp @@ -136,18 +136,18 @@ ImageWrapper::GetOrientation() { return NS_IMETHODIMP_(Resolution) ImageWrapper::GetResolution() { return mInnerImage->GetResolution(); } NS_IMETHODIMP ImageWrapper::GetType(uint16_t* aType) { return mInnerImage->GetType(aType); } NS_IMETHODIMP -ImageWrapper::GetProviderId(uint32_t* aId) { - return mInnerImage->GetProviderId(aId); +ImageWrapper::GetProducerId(uint32_t* aId) { + return mInnerImage->GetProducerId(aId); } NS_IMETHODIMP ImageWrapper::GetAnimated(bool* aAnimated) { return mInnerImage->GetAnimated(aAnimated); } NS_IMETHODIMP_(already_AddRefed<SourceSurface>) @@ -166,24 +166,24 @@ ImageWrapper::WillDrawOpaqueNow() { retu NS_IMETHODIMP_(bool) ImageWrapper::IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) { return mInnerImage->IsImageContainerAvailable(aRenderer, aFlags); } NS_IMETHODIMP_(ImgDrawResult) -ImageWrapper::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { - return mInnerImage->GetImageProvider(aRenderer, aSize, aSVGContext, aRegion, - aFlags, aProvider); +ImageWrapper::GetImageContainerAtSize(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aOutContainer) { + return mInnerImage->GetImageContainerAtSize(aRenderer, aSize, aSVGContext, + aRegion, aFlags, aOutContainer); } NS_IMETHODIMP_(ImgDrawResult) ImageWrapper::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, SamplingFilter aSamplingFilter, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags, float aOpacity) {
--- a/image/OrientedImage.cpp +++ b/image/OrientedImage.cpp @@ -151,31 +151,30 @@ OrientedImage::IsImageContainerAvailable uint32_t aFlags) { if (mOrientation.IsIdentity()) { return InnerImage()->IsImageContainerAvailable(aRenderer, aFlags); } return false; } NS_IMETHODIMP_(ImgDrawResult) -OrientedImage::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { +OrientedImage::GetImageContainerAtSize( + WindowRenderer* aRenderer, const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, + layers::ImageContainer** aOutContainer) { // XXX(seth): We currently don't have a way of orienting the result of // GetImageContainer. We work around this by always returning null, but if it // ever turns out that OrientedImage is widely used on codepaths that can // actually benefit from GetImageContainer, it would be a good idea to fix // that method for performance reasons. if (mOrientation.IsIdentity()) { - return InnerImage()->GetImageProvider(aRenderer, aSize, aSVGContext, - aRegion, aFlags, aProvider); + return InnerImage()->GetImageContainerAtSize( + aRenderer, aSize, aSVGContext, aRegion, aFlags, aOutContainer); } return ImgDrawResult::NOT_SUPPORTED; } struct MatrixBuilder { explicit MatrixBuilder(bool aInvert) : mInvert(aInvert) {}
--- a/image/OrientedImage.h +++ b/image/OrientedImage.h @@ -36,20 +36,20 @@ class OrientedImage : public ImageWrappe GetFrame(uint32_t aWhichFrame, uint32_t aFlags) override; NS_IMETHOD_(already_AddRefed<SourceSurface>) GetFrameAtSize(const gfx::IntSize& aSize, uint32_t aWhichFrame, uint32_t aFlags) override; NS_IMETHOD_(bool) IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) override; NS_IMETHOD_(ImgDrawResult) - GetImageProvider(WindowRenderer* aRenderer, const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, - WebRenderImageProvider** aProvider) override; + GetImageContainerAtSize(WindowRenderer* aRenderer, const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aFlags, + layers::ImageContainer** aOutContainer) override; NS_IMETHOD_(ImgDrawResult) Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, gfx::SamplingFilter aSamplingFilter, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags, float aOpacity) override; NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect(const nsIntRect& aRect) override; nsIntSize OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
--- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -268,20 +268,20 @@ NS_IMETHODIMP RasterImage::GetType(uint16_t* aType) { NS_ENSURE_ARG_POINTER(aType); *aType = imgIContainer::TYPE_RASTER; return NS_OK; } NS_IMETHODIMP -RasterImage::GetProviderId(uint32_t* aId) { +RasterImage::GetProducerId(uint32_t* aId) { NS_ENSURE_ARG_POINTER(aId); - *aId = ImageResource::GetImageProviderId(); + *aId = ImageResource::GetImageProducerId(); return NS_OK; } LookupResult RasterImage::LookupFrameInternal(const OrientedIntSize& aSize, uint32_t aFlags, PlaybackType aPlaybackType, bool aMarkUsed) { if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) { @@ -474,16 +474,17 @@ void RasterImage::OnSurfaceDiscarded(con eventTarget->Dispatch(ev.forget(), NS_DISPATCH_NORMAL); } void RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded) { MOZ_ASSERT(NS_IsMainThread()); if (aAnimatedFramesDiscarded && mAnimationState) { MOZ_ASSERT(StaticPrefs::image_mem_animated_discardable_AtStartup()); + ReleaseImageContainer(); IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize()); auto dirtyRect = OrientedIntRect::FromUnknownRect(rect); NotifyProgress(NoProgress, dirtyRect); } if (mProgressTracker) { @@ -540,111 +541,136 @@ RasterImage::GetFirstFrameDelay() { NS_IMETHODIMP_(already_AddRefed<SourceSurface>) RasterImage::GetFrame(uint32_t aWhichFrame, uint32_t aFlags) { return GetFrameAtSize(mSize.ToUnknownSize(), aWhichFrame, aFlags); } NS_IMETHODIMP_(already_AddRefed<SourceSurface>) RasterImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame, uint32_t aFlags) { - MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); - AutoProfilerImagePaintMarker PROFILER_RAII(this); #ifdef DEBUG NotifyDrawingObservers(); #endif - if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE || mError) { - return nullptr; + auto result = + GetFrameInternal(aSize, Nothing(), Nothing(), aWhichFrame, aFlags); + return mozilla::Get<2>(result).forget(); +} + +Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>> +RasterImage::GetFrameInternal(const IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aWhichFrame, uint32_t aFlags) { + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); + + auto size = OrientedIntSize::FromUnknownSize(aSize); + + if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) { + return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>()); } - auto size = OrientedIntSize::FromUnknownSize(aSize); + if (mError) { + return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>()); + } // Get the frame. If it's not there, it's probably the caller's fault for // not waiting for the data to be loaded from the network or not passing // FLAG_SYNC_DECODE. LookupResult result = LookupFrame(size, aFlags, ToPlaybackType(aWhichFrame), /* aMarkUsed = */ true); + + // The surface cache may have suggested we use a different size than the + // given size in the future. This may or may not be accompanied by an + // actual surface, depending on what it has in its cache. + auto suggestedSize = OrientedIntSize::FromUnknownSize(result.SuggestedSize()); + if (suggestedSize.IsEmpty()) { + suggestedSize = size; + } + MOZ_ASSERT_IF(result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST, + suggestedSize != size); + if (!result) { // The OS threw this frame away and we couldn't redecode it. - return nullptr; + return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, + suggestedSize.ToUnknownSize(), RefPtr<SourceSurface>()); + } + + RefPtr<SourceSurface> surface = result.Surface()->GetSourceSurface(); + if (!result.Surface()->IsFinished()) { + return MakeTuple(ImgDrawResult::INCOMPLETE, suggestedSize.ToUnknownSize(), + std::move(surface)); + } + + return MakeTuple(ImgDrawResult::SUCCESS, suggestedSize.ToUnknownSize(), + std::move(surface)); +} + +Tuple<ImgDrawResult, IntSize> RasterImage::GetImageContainerSize( + WindowRenderer* aRenderer, const IntSize& aRequestedSize, uint32_t aFlags) { + if (!LoadHasSize()) { + return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0)); } - return result.Surface()->GetSourceSurface(); + if (aRequestedSize.IsEmpty()) { + return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0)); + } + + // We check the minimum size because while we support downscaling, we do not + // support upscaling. If aRequestedSize > mSize, we will never give a larger + // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize, + // we still want to use image containers if aRequestedSize <= maxTextureSize. + int32_t maxTextureSize = aRenderer->GetMaxTextureSize(); + if (min(mSize.width, aRequestedSize.width) > maxTextureSize || + min(mSize.height, aRequestedSize.height) > maxTextureSize) { + return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0)); + } + + auto requestedSize = OrientedIntSize::FromUnknownSize(aRequestedSize); + if (!CanDownscaleDuringDecode(requestedSize, aFlags)) { + return MakeTuple(ImgDrawResult::SUCCESS, mSize.ToUnknownSize()); + } + + return MakeTuple(ImgDrawResult::SUCCESS, aRequestedSize); } NS_IMETHODIMP_(bool) RasterImage::IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) { return LoadHasSize(); } NS_IMETHODIMP_(ImgDrawResult) -RasterImage::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aRenderer); - - if (mError) { - return ImgDrawResult::BAD_IMAGE; - } - - if (!LoadHasSize()) { - return ImgDrawResult::NOT_READY; - } - - if (aSize.IsEmpty()) { - return ImgDrawResult::BAD_ARGS; - } - - // We check the minimum size because while we support downscaling, we do not - // support upscaling. If aRequestedSize > mSize, we will never give a larger - // surface than mSize. If mSize > aRequestedSize, and mSize > maxTextureSize, - // we still want to use image containers if aRequestedSize <= maxTextureSize. - int32_t maxTextureSize = aRenderer->GetMaxTextureSize(); - if (min(mSize.width, aSize.width) > maxTextureSize || - min(mSize.height, aSize.height) > maxTextureSize) { - return ImgDrawResult::NOT_SUPPORTED; - } - - AutoProfilerImagePaintMarker PROFILER_RAII(this); -#ifdef DEBUG - NotifyDrawingObservers(); -#endif - - // Get the frame. If it's not there, it's probably the caller's fault for - // not waiting for the data to be loaded from the network or not passing - // FLAG_SYNC_DECODE. - LookupResult result = LookupFrame(OrientedIntSize::FromUnknownSize(aSize), - aFlags, PlaybackType::eAnimated, - /* aMarkUsed = */ true); - if (!result) { - // The OS threw this frame away and we couldn't redecode it. - return ImgDrawResult::NOT_READY; - } - - if (!result.Surface()->IsFinished()) { - result.Surface().TakeProvider(aProvider); - return ImgDrawResult::INCOMPLETE; - } - - result.Surface().TakeProvider(aProvider); - return ImgDrawResult::SUCCESS; +RasterImage::GetImageContainerAtSize(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aOutContainer) { + // We do not pass in the given SVG context because in theory it could differ + // between calls, but actually have no impact on the actual contents of the + // image container. + return GetImageContainerImpl(aRenderer, aSize, Nothing(), Nothing(), aFlags, + aOutContainer); } size_t RasterImage::SizeOfSourceWithComputedFallback( SizeOfState& aState) const { return mSourceBuffer->SizeOfIncludingThisWithComputedFallback( aState.mMallocSizeOf); } +void RasterImage::CollectSizeOfSurfaces( + nsTArray<SurfaceMemoryCounter>& aCounters, + MallocSizeOf aMallocSizeOf) const { + SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf); + ImageResource::CollectSizeOfSurfaces(aCounters, aMallocSizeOf); +} + bool RasterImage::SetMetadata(const ImageMetadata& aMetadata, bool aFromMetadataDecode) { MOZ_ASSERT(NS_IsMainThread()); if (mError) { return true; } @@ -962,16 +988,18 @@ void RasterImage::Discard() { MOZ_ASSERT(!mAnimationState || StaticPrefs::image_mem_animated_discardable_AtStartup(), "Asked to discard for animated image"); // Delete all the decoded frames. SurfaceCache::RemoveImage(ImageKey(this)); if (mAnimationState) { + ReleaseImageContainer(); + IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize()); auto dirtyRect = OrientedIntRect::FromUnknownRect(rect); NotifyProgress(NoProgress, dirtyRect); } // Notify that we discarded. if (mProgressTracker) { @@ -1565,16 +1593,23 @@ void RasterImage::NotifyProgress( if (mAnimationState) { IntRect rect = mAnimationState->UpdateState(this, mSize.ToUnknownSize()); invalidRect.UnionRect(invalidRect, OrientedIntRect::FromUnknownRect(rect)); } } + const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags(); + + if (!invalidRect.IsEmpty() && wasDefaultFlags) { + // Update our image container since we're invalidating. + UpdateImageContainer(Some(invalidRect.ToUnknownRect())); + } + // Tell the observers what happened. image->mProgressTracker->SyncNotifyProgress(aProgress, invalidRect.ToUnknownRect()); } void RasterImage::NotifyDecodeComplete( const DecoderFinalStatus& aStatus, const ImageMetadata& aMetadata, const DecoderTelemetry& aTelemetry, Progress aProgress,
--- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -161,16 +161,18 @@ class RasterImage final : public ImageRe virtual nsresult StartAnimation() override; virtual nsresult StopAnimation() override; // Methods inherited from Image virtual void OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey) override; virtual size_t SizeOfSourceWithComputedFallback( SizeOfState& aState) const override; + virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters, + MallocSizeOf aMallocSizeOf) const override; /* Triggers discarding. */ void Discard(); ////////////////////////////////////////////////////////////////////////////// // Decoder callbacks. ////////////////////////////////////////////////////////////////////////////// @@ -287,16 +289,26 @@ class RasterImage final : public ImageRe bool aMarkUsed); ImgDrawResult DrawInternal(DrawableSurface&& aFrameRef, gfxContext* aContext, const OrientedIntSize& aSize, const ImageRegion& aRegion, gfx::SamplingFilter aSamplingFilter, uint32_t aFlags, float aOpacity); + Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>> + GetFrameInternal(const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aWhichFrame, + uint32_t aFlags) override; + + Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize( + WindowRenderer* aRenderer, const gfx::IntSize& aSize, + uint32_t aFlags) override; + ////////////////////////////////////////////////////////////////////////////// // Decoding. ////////////////////////////////////////////////////////////////////////////// /** * Creates and runs a decoder, either synchronously or asynchronously * according to @aFlags. Decodes at the provided target size @aSize, using * decode flags @aFlags. Performs a single-frame decode of this image unless
rename from image/BlobSurfaceProvider.cpp rename to image/SourceSurfaceBlobImage.cpp --- a/image/BlobSurfaceProvider.cpp +++ b/image/SourceSurfaceBlobImage.cpp @@ -1,160 +1,154 @@ /* -*- 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 "BlobSurfaceProvider.h" +#include "SourceSurfaceBlobImage.h" #include "AutoRestoreSVGState.h" #include "ImageRegion.h" #include "SVGDocumentWrapper.h" #include "mozilla/PresShell.h" #include "mozilla/dom/Document.h" #include "mozilla/gfx/2D.h" #include "mozilla/layers/IpcResourceUpdateQueue.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/layers/WebRenderDrawEventRecorder.h" using namespace mozilla::gfx; using namespace mozilla::layers; namespace mozilla::image { -BlobSurfaceProvider::BlobSurfaceProvider( - const ImageKey aImageKey, const SurfaceKey& aSurfaceKey, - image::SVGDocumentWrapper* aSVGDocumentWrapper, uint32_t aImageFlags) - : ISurfaceProvider(aImageKey, aSurfaceKey, - AvailabilityState::StartAvailable()), - mSVGDocumentWrapper(aSVGDocumentWrapper), +SourceSurfaceBlobImage::SourceSurfaceBlobImage( + image::SVGDocumentWrapper* aSVGDocumentWrapper, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, const IntSize& aSize, + uint32_t aWhichFrame, uint32_t aImageFlags) + : mSVGDocumentWrapper(aSVGDocumentWrapper), + mSVGContext(aSVGContext), + mRegion(aRegion), + mSize(aSize), + mWhichFrame(aWhichFrame), mImageFlags(aImageFlags) { MOZ_ASSERT(mSVGDocumentWrapper); + MOZ_ASSERT(aWhichFrame <= imgIContainer::FRAME_MAX_VALUE); MOZ_ASSERT(aImageFlags & imgIContainer::FLAG_RECORD_BLOB); } -BlobSurfaceProvider::~BlobSurfaceProvider() { +SourceSurfaceBlobImage::~SourceSurfaceBlobImage() { if (NS_IsMainThread()) { DestroyKeys(mKeys); return; } NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper", mSVGDocumentWrapper.forget()); NS_DispatchToMainThread( NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys", [keys = std::move(mKeys)] { DestroyKeys(keys); })); } -/* static */ void BlobSurfaceProvider::DestroyKeys( +/* static */ void SourceSurfaceBlobImage::DestroyKeys( const AutoTArray<BlobImageKeyData, 1>& aKeys) { for (const auto& entry : aKeys) { if (!entry.mManager->IsDestroyed()) { entry.mManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard( entry.mBlobKey); } } } -nsresult BlobSurfaceProvider::UpdateKey( - layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, wr::ImageKey& aKey) { +Maybe<wr::BlobImageKey> SourceSurfaceBlobImage::UpdateKey( + WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources) { MOZ_ASSERT(NS_IsMainThread()); - layers::WebRenderLayerManager* manager = aManager->LayerManager(); - MOZ_ASSERT(manager); - Maybe<wr::BlobImageKey> key; auto i = mKeys.Length(); while (i > 0) { --i; BlobImageKeyData& entry = mKeys[i]; if (entry.mManager->IsDestroyed()) { mKeys.RemoveElementAt(i); - } else if (entry.mManager == manager) { - WebRenderBridgeChild* wrBridge = manager->WrBridge(); + } else if (entry.mManager == aManager) { + WebRenderBridgeChild* wrBridge = aManager->WrBridge(); MOZ_ASSERT(wrBridge); bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey); if (ownsKey && !entry.mDirty) { key.emplace(entry.mBlobKey); continue; } // Even if the manager is the same, its underlying WebRenderBridgeChild // can change state. Either our namespace differs, and our old key has // already been discarded, or the blob has changed. Either way, we need // to rerecord it. - auto newEntry = RecordDrawing(manager, aResources, + auto newEntry = RecordDrawing(aManager, aResources, ownsKey ? Some(entry.mBlobKey) : Nothing()); if (!newEntry) { if (ownsKey) { - aManager->AddBlobImageKeyForDiscard(entry.mBlobKey); + aManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard( + entry.mBlobKey); } mKeys.RemoveElementAt(i); continue; } key.emplace(newEntry.ref().mBlobKey); entry = std::move(newEntry.ref()); MOZ_ASSERT(!entry.mDirty); } } // We didn't find an entry. Attempt to record the blob with a new key. if (!key) { - auto newEntry = RecordDrawing(manager, aResources, Nothing()); + auto newEntry = RecordDrawing(aManager, aResources, Nothing()); if (newEntry) { key.emplace(newEntry.ref().mBlobKey); mKeys.AppendElement(std::move(newEntry.ref())); } } - if (key) { - aKey = wr::AsImageKey(key.value()); - return NS_OK; - } - - return NS_ERROR_FAILURE; + return key; } -void BlobSurfaceProvider::InvalidateRecording() { +void SourceSurfaceBlobImage::MarkDirty() { MOZ_ASSERT(NS_IsMainThread()); auto i = mKeys.Length(); while (i > 0) { --i; BlobImageKeyData& entry = mKeys[i]; if (entry.mManager->IsDestroyed()) { mKeys.RemoveElementAt(i); } else { entry.mDirty = true; } } } -Maybe<BlobImageKeyData> BlobSurfaceProvider::RecordDrawing( +Maybe<BlobImageKeyData> SourceSurfaceBlobImage::RecordDrawing( WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources, Maybe<wr::BlobImageKey> aBlobKey) { MOZ_ASSERT(!aManager->IsDestroyed()); if (mSVGDocumentWrapper->IsDrawing()) { return Nothing(); } // This is either our first pass, or we have a stale key requiring us to // re-record the SVG image draw commands. auto* rootManager = aManager->GetRenderRootStateManager(); auto* wrBridge = aManager->WrBridge(); - const auto& size = GetSurfaceKey().Size(); - const auto& region = GetSurfaceKey().Region(); - const auto& svgContext = GetSurfaceKey().SVGContext(); - - IntRect imageRect = region ? region->Rect() : IntRect(IntPoint(0, 0), size); + IntRect imageRect = + mRegion ? mRegion->Rect() : IntRect(IntPoint(0, 0), mSize); IntRect imageRectOrigin = imageRect - imageRect.TopLeft(); std::vector<RefPtr<ScaledFont>> fonts; bool validFonts = true; RefPtr<WebRenderDrawEventRecorder> recorder = MakeAndAddRef<WebRenderDrawEventRecorder>( [&](MemStream& aStream, std::vector<RefPtr<ScaledFont>>& aScaledFonts) { @@ -179,25 +173,25 @@ Maybe<BlobImageKeyData> BlobSurfaceProvi gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); RefPtr<DrawTarget> dt = Factory::CreateRecordingDrawTarget(recorder, dummyDt, imageRectOrigin); if (!dt || !dt->IsValid()) { return Nothing(); } - bool contextPaint = svgContext && svgContext->GetContextPaint(); + bool contextPaint = mSVGContext && mSVGContext->GetContextPaint(); - float animTime = (GetSurfaceKey().Playback() == PlaybackType::eStatic) + float animTime = (mWhichFrame == imgIContainer::FRAME_FIRST) ? 0.0f : mSVGDocumentWrapper->GetCurrentTimeAsFloat(); - IntSize viewportSize = size; - if (svgContext) { - auto cssViewportSize = svgContext->GetViewportSize(); + IntSize viewportSize = mSize; + if (mSVGContext) { + auto cssViewportSize = mSVGContext->GetViewportSize(); if (cssViewportSize) { // XXX losing unit viewportSize.SizeTo(cssViewportSize->width, cssViewportSize->height); } } { // Get (& sanity-check) the helper-doc's presShell @@ -207,34 +201,34 @@ Maybe<BlobImageKeyData> BlobSurfaceProvi nsPresContext* presContext = presShell->GetPresContext(); MOZ_ASSERT(presContext, "pres shell w/out pres context"); auto* doc = presShell->GetDocument(); [[maybe_unused]] nsIURI* uri = doc ? doc->GetDocumentURI() : nullptr; AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING( "SVG Image recording", GRAPHICS, nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect.x, imageRect.y, - imageRect.width, imageRect.height, size.width, - size.height, + imageRect.width, imageRect.height, mSize.width, + mSize.height, uri ? uri->GetSpecOrDefault().get() : "N/A")); - AutoRestoreSVGState autoRestore(svgContext, animTime, mSVGDocumentWrapper, + AutoRestoreSVGState autoRestore(mSVGContext, animTime, mSVGDocumentWrapper, contextPaint); mSVGDocumentWrapper->UpdateViewportBounds(viewportSize); mSVGDocumentWrapper->FlushImageTransformInvalidation(); RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt); MOZ_ASSERT(ctx); // Already checked the draw target above. nsRect svgRect; auto auPerDevPixel = presContext->AppUnitsPerDevPixel(); - if (size != viewportSize) { - auto scaleX = double(size.width) / viewportSize.width; - auto scaleY = double(size.height) / viewportSize.height; + if (mSize != viewportSize) { + auto scaleX = double(mSize.width) / viewportSize.width; + auto scaleY = double(mSize.height) / viewportSize.height; ctx->SetMatrix(Matrix::Scaling(float(scaleX), float(scaleY))); auto scaledVisibleRect = IntRectToRect(imageRect); scaledVisibleRect.Scale(float(auPerDevPixel / scaleX), float(auPerDevPixel / scaleY)); scaledVisibleRect.Round(); svgRect.SetRect( int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y),
rename from image/BlobSurfaceProvider.h rename to image/SourceSurfaceBlobImage.h --- a/image/BlobSurfaceProvider.h +++ b/image/SourceSurfaceBlobImage.h @@ -1,23 +1,22 @@ /* -*- 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/. */ -#ifndef MOZILLA_IMAGE_BLOBSURFACEPROVIDER_H_ -#define MOZILLA_IMAGE_BLOBSURFACEPROVIDER_H_ +#ifndef MOZILLA_IMAGE_SOURCESURFACEBLOBIMAGE_H_ +#define MOZILLA_IMAGE_SOURCESURFACEBLOBIMAGE_H_ #include "mozilla/Maybe.h" #include "mozilla/SVGImageContext.h" #include "mozilla/gfx/2D.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "ImageRegion.h" -#include "ISurfaceProvider.h" #include <vector> namespace mozilla { namespace image { class BlobImageKeyData final { public: @@ -66,75 +65,68 @@ namespace mozilla { namespace wr { class IpcResourceUpdateQueue; } // namespace wr namespace image { class SVGDocumentWrapper; /** - * An ISurfaceProvider that manages blob recordings of SVG images. Unlike the - * rasterized ISurfaceProviders, it only provides a recording which may be - * replayed in the compositor process by WebRender. It may be invalidated - * directly in order to reuse the resource ids and underlying buffers when the - * SVG image has changed (e.g. it is animated). + * This class is used to wrap blob recordings stored in ImageContainers, used + * by SVG images. Unlike rasterized images stored in SourceSurfaceSharedData, + * each SourceSurfaceBlobImage can only be used by one WebRenderLayerManager + * because the recording is tied to a particular instance. */ -class BlobSurfaceProvider final : public ISurfaceProvider { +class SourceSurfaceBlobImage final : public gfx::SourceSurface { public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobSurfaceProvider, override) + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceBlobImage, override) + + SourceSurfaceBlobImage(SVGDocumentWrapper* aSVGDocumentWrapper, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + const gfx::IntSize& aSize, uint32_t aWhichFrame, + uint32_t aImageFlags); - BlobSurfaceProvider(ImageKey aImageKey, const SurfaceKey& aSurfaceKey, - SVGDocumentWrapper* aSVGDocumentWrapper, - uint32_t aImageFlags); + Maybe<wr::BlobImageKey> UpdateKey(layers::WebRenderLayerManager* aManager, + wr::IpcResourceUpdateQueue& aResources); + + void MarkDirty(); - bool IsFinished() const override { return true; } - - size_t LogicalSizeInBytes() const override { - const gfx::IntSize& size = GetSurfaceKey().Size(); - return size.width * size.height * sizeof(uint32_t); + gfx::SurfaceType GetType() const override { + return gfx::SurfaceType::BLOB_IMAGE; + } + gfx::IntSize GetSize() const override { return mSize; } + gfx::SurfaceFormat GetFormat() const override { + return gfx::SurfaceFormat::OS_RGBA; + } + already_AddRefed<gfx::DataSourceSurface> GetDataSurface() override { + return nullptr; } - nsresult UpdateKey(layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, - wr::ImageKey& aKey) override; - - void InvalidateRecording() override; - - void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, - const AddSizeOfCb& aCallback) override { - AddSizeOfCbData metadata; - metadata.mFinished = true; - metadata.mHeapBytes += mKeys.ShallowSizeOfExcludingThis(aMallocSizeOf); - - gfx::SourceSurface::SizeOfInfo info; - info.AddType(gfx::SurfaceType::BLOB_IMAGE); - metadata.Accumulate(info); - - aCallback(metadata); + void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, + SizeOfInfo& aInfo) const override { + aInfo.AddType(gfx::SurfaceType::BLOB_IMAGE); + aInfo.mHeapBytes += mKeys.ShallowSizeOfExcludingThis(aMallocSizeOf); } - protected: - DrawableFrameRef DrawableRef(size_t aFrame) override { - MOZ_ASSERT_UNREACHABLE("BlobSurfaceProvider::DrawableRef not supported!"); - return DrawableFrameRef(); - } - bool IsLocked() const override { return true; } - void SetLocked(bool) override {} - private: - ~BlobSurfaceProvider() override; + ~SourceSurfaceBlobImage() override; Maybe<BlobImageKeyData> RecordDrawing(layers::WebRenderLayerManager* aManager, wr::IpcResourceUpdateQueue& aResources, Maybe<wr::BlobImageKey> aBlobKey); static void DestroyKeys(const AutoTArray<BlobImageKeyData, 1>& aKeys); AutoTArray<BlobImageKeyData, 1> mKeys; RefPtr<image::SVGDocumentWrapper> mSVGDocumentWrapper; + Maybe<SVGImageContext> mSVGContext; + Maybe<ImageIntRegion> mRegion; + gfx::IntSize mSize; + uint32_t mWhichFrame; uint32_t mImageFlags; }; } // namespace image } // namespace mozilla -#endif /* MOZILLA_IMAGE_BLOBSURFACEPROVIDER_H_ */ +#endif /* MOZILLA_IMAGE_SOURCESURFACEBLOBIMAGE_H_ */
--- a/image/SurfaceCache.cpp +++ b/image/SurfaceCache.cpp @@ -171,18 +171,16 @@ class CachedSurface { CostEntry GetCostEntry() { return image::CostEntry(WrapNotNull(this), mProvider->LogicalSizeInBytes()); } size_t ShallowSizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { return aMallocSizeOf(this) + aMallocSizeOf(mProvider.get()); } - void InvalidateRecording() { mProvider->InvalidateRecording(); } - // A helper type used by SurfaceCacheImpl::CollectSizeOfSurfaces. struct MOZ_STACK_CLASS SurfaceMemoryReport { SurfaceMemoryReport(nsTArray<SurfaceMemoryCounter>& aCounters, MallocSizeOf aMallocSizeOf) : mCounters(aCounters), mMallocSizeOf(aMallocSizeOf) {} void Add(NotNull<CachedSurface*> aCachedSurface, bool aIsFactor2) { if (aCachedSurface->IsPlaceholder()) { @@ -190,20 +188,20 @@ class CachedSurface { } // Record the memory used by the ISurfaceProvider. This may not have a // straightforward relationship to the size of the surface that // DrawableRef() returns if the surface is generated dynamically. (i.e., // for surfaces with PlaybackType::eAnimated.) aCachedSurface->mProvider->AddSizeOfExcludingThis( mMallocSizeOf, [&](ISurfaceProvider::AddSizeOfCbData& aMetadata) { - SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(), - aCachedSurface->IsLocked(), - aCachedSurface->CannotSubstitute(), - aIsFactor2, aMetadata.mFinished); + SurfaceMemoryCounter counter( + aCachedSurface->GetSurfaceKey(), aMetadata.mSurface, + aCachedSurface->IsLocked(), aCachedSurface->CannotSubstitute(), + aIsFactor2, aMetadata.mFinished); counter.Values().SetDecodedHeap(aMetadata.mHeapBytes); counter.Values().SetDecodedNonHeap(aMetadata.mNonHeapBytes); counter.Values().SetDecodedUnknown(aMetadata.mUnknownBytes); counter.Values().SetExternalHandles(aMetadata.mExternalHandles); counter.Values().SetFrameIndex(aMetadata.mIndex); counter.Values().SetExternalId(aMetadata.mExternalId); counter.Values().SetSurfaceTypes(aMetadata.mTypes); @@ -273,24 +271,18 @@ class ImageSurfaceCache { bytes += value->ShallowSizeOfIncludingThis(aMallocSizeOf); } return bytes; } [[nodiscard]] bool Insert(NotNull<CachedSurface*> aSurface) { MOZ_ASSERT(!mLocked || aSurface->IsPlaceholder() || aSurface->IsLocked(), "Inserting an unlocked surface for a locked image"); - const auto& surfaceKey = aSurface->GetSurfaceKey(); - if (surfaceKey.Region()) { - // We don't allow substitutes for surfaces with regions, so we don't want - // to allow factor of 2 mode pruning to release these surfaces. - aSurface->SetCannotSubstitute(); - } - return mSurfaces.InsertOrUpdate(surfaceKey, RefPtr<CachedSurface>{aSurface}, - fallible); + return mSurfaces.InsertOrUpdate(aSurface->GetSurfaceKey(), + RefPtr<CachedSurface>{aSurface}, fallible); } already_AddRefed<CachedSurface> Remove(NotNull<CachedSurface*> aSurface) { MOZ_ASSERT(mSurfaces.GetWeak(aSurface->GetSurfaceKey()), "Should not be removing a surface we don't have"); RefPtr<CachedSurface> surface; mSurfaces.Remove(aSurface->GetSurfaceKey(), getter_AddRefs(surface)); @@ -329,20 +321,16 @@ class ImageSurfaceCache { const SurfaceKey& aIdealKey) { // Try for an exact match first. RefPtr<CachedSurface> exactMatch; mSurfaces.Get(aIdealKey, getter_AddRefs(exactMatch)); if (exactMatch) { if (exactMatch->IsDecoded()) { return MakeTuple(exactMatch.forget(), MatchType::EXACT, IntSize()); } - } else if (aIdealKey.Region()) { - // We cannot substitute if we have a region. Allow it to create an exact - // match. - return MakeTuple(exactMatch.forget(), MatchType::NOT_FOUND, IntSize()); } else if (!mFactor2Mode) { // If no exact match is found, and we are not in factor of 2 mode, then // we know that we will trigger a decode because at best we will provide // a substitute. Make sure we switch now to factor of 2 mode if necessary. MaybeSetFactor2Mode(); } // Try for a best match second, if using compact. @@ -360,18 +348,18 @@ class ImageSurfaceCache { } // There's no perfect match, so find the best match we can. RefPtr<CachedSurface> bestMatch; for (const auto& value : Values()) { NotNull<CachedSurface*> current = WrapNotNull(value); const SurfaceKey& currentKey = current->GetSurfaceKey(); - // We never match a placeholder or a surface with a region. - if (current->IsPlaceholder() || currentKey.Region()) { + // We never match a placeholder. + if (current->IsPlaceholder()) { continue; } // Matching the playback type and SVG context is required. if (currentKey.Playback() != aIdealKey.Playback() || currentKey.SVGContext() != aIdealKey.SVGContext()) { continue; } // Matching the flags is required. @@ -537,38 +525,16 @@ class ImageSurfaceCache { } // We should never leave factor of 2 mode due to pruning in of itself, but // if we discarded surfaces due to the volatile buffers getting released, // it is possible. AfterMaybeRemove(); } - template <typename Function> - bool Invalidate(Function&& aRemoveCallback) { - // Remove all non-blob recordings from the cache. Invalidate any blob - // recordings. - bool foundRecording = false; - for (auto iter = mSurfaces.Iter(); !iter.Done(); iter.Next()) { - NotNull<CachedSurface*> current = WrapNotNull(iter.UserData()); - - if (current->GetSurfaceKey().Flags() & SurfaceFlags::RECORD_BLOB) { - foundRecording = true; - current->InvalidateRecording(); - continue; - } - - aRemoveCallback(current); - iter.Remove(); - } - - AfterMaybeRemove(); - return foundRecording; - } - IntSize SuggestedSize(const IntSize& aSize) const { IntSize suggestedSize = SuggestedSizeInternal(aSize); if (mIsVectorImage) { suggestedSize = SurfaceCache::ClampVectorSize(suggestedSize); } return suggestedSize; } @@ -1045,18 +1011,17 @@ class SurfaceCacheImpl final : public ns Remove(WrapNotNull(surface), /* aStopTracking */ true, aAutoLock); } MOZ_ASSERT_IF(matchType == MatchType::EXACT, surface->GetSurfaceKey() == aSurfaceKey); MOZ_ASSERT_IF( matchType == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND || matchType == MatchType::SUBSTITUTE_BECAUSE_PENDING, - surface->GetSurfaceKey().Region() == aSurfaceKey.Region() && - surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() && + surface->GetSurfaceKey().SVGContext() == aSurfaceKey.SVGContext() && surface->GetSurfaceKey().Playback() == aSurfaceKey.Playback() && surface->GetSurfaceKey().Flags() == aSurfaceKey.Flags()); if (matchType == MatchType::EXACT || matchType == MatchType::SUBSTITUTE_BECAUSE_BEST) { if (aMarkUsed && !MarkUsed(WrapNotNull(surface), WrapNotNull(cache), aAutoLock)) { Remove(WrapNotNull(surface), /* aStopTracking */ false, aAutoLock); @@ -1162,34 +1127,16 @@ class SurfaceCacheImpl final : public ns StopTracking(aSurface, /* aIsTracked */ true, aAutoLock); // Individual surfaces must be freed outside the lock. mCachedSurfacesDiscard.AppendElement(aSurface); }); MaybeRemoveEmptyCache(aImageKey, cache); } - bool InvalidateImage(const ImageKey aImageKey, - const StaticMutexAutoLock& aAutoLock) { - RefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey); - if (!cache) { - return false; // No cached surfaces for this image, so nothing to do. - } - - bool rv = cache->Invalidate( - [this, &aAutoLock](NotNull<CachedSurface*> aSurface) -> void { - StopTracking(aSurface, /* aIsTracked */ true, aAutoLock); - // Individual surfaces must be freed outside the lock. - mCachedSurfacesDiscard.AppendElement(aSurface); - }); - - MaybeRemoveEmptyCache(aImageKey, cache); - return rv; - } - void DiscardAll(const StaticMutexAutoLock& aAutoLock) { // Remove in order of cost because mCosts is an array and the other data // structures are all hash tables. Note that locked surfaces are not // removed, since they aren't present in mCosts. while (!mCosts.IsEmpty()) { Remove(mCosts.LastElement().Surface(), /* aStopTracking */ true, aAutoLock); } @@ -1766,30 +1713,16 @@ void SurfaceCache::PruneImage(const Imag if (sInstance) { sInstance->PruneImage(aImageKey, lock); sInstance->TakeDiscard(discard, lock); } } } /* static */ -bool SurfaceCache::InvalidateImage(const ImageKey aImageKey) { - nsTArray<RefPtr<CachedSurface>> discard; - bool rv = false; - { - StaticMutexAutoLock lock(sInstanceMutex); - if (sInstance) { - rv = sInstance->InvalidateImage(aImageKey, lock); - sInstance->TakeDiscard(discard, lock); - } - } - return rv; -} - -/* static */ void SurfaceCache::DiscardAll() { nsTArray<RefPtr<CachedSurface>> discard; { StaticMutexAutoLock lock(sInstanceMutex); if (sInstance) { sInstance->DiscardAll(lock); sInstance->TakeDiscard(discard, lock); }
--- a/image/SurfaceCache.h +++ b/image/SurfaceCache.h @@ -16,130 +16,116 @@ #include "mozilla/MemoryReporting.h" // for MallocSizeOf #include "mozilla/NotNull.h" #include "mozilla/SVGImageContext.h" // for SVGImageContext #include "mozilla/gfx/2D.h" // for SourceSurface #include "mozilla/gfx/Point.h" // for mozilla::gfx::IntSize #include "gfx2DGlue.h" #include "gfxPoint.h" // for gfxSize #include "nsCOMPtr.h" // for already_AddRefed -#include "ImageRegion.h" #include "PlaybackType.h" #include "SurfaceFlags.h" namespace mozilla { namespace image { -class ImageResource; +class Image; class ISurfaceProvider; class LookupResult; class SurfaceCacheImpl; struct SurfaceMemoryCounter; /* * ImageKey contains the information we need to look up all SurfaceCache entries * for a particular image. */ -using ImageKey = ImageResource*; +typedef Image* ImageKey; /* * SurfaceKey contains the information we need to look up a specific * SurfaceCache entry. Together with an ImageKey, this uniquely identifies the * surface. * * Callers should construct a SurfaceKey using the appropriate helper function * for their image type - either RasterSurfaceKey or VectorSurfaceKey. */ class SurfaceKey { typedef gfx::IntSize IntSize; public: bool operator==(const SurfaceKey& aOther) const { - return aOther.mSize == mSize && aOther.mRegion == mRegion && - aOther.mSVGContext == mSVGContext && aOther.mPlayback == mPlayback && - aOther.mFlags == mFlags; + return aOther.mSize == mSize && aOther.mSVGContext == mSVGContext && + aOther.mPlayback == mPlayback && aOther.mFlags == mFlags; } PLDHashNumber Hash() const { PLDHashNumber hash = HashGeneric(mSize.width, mSize.height); - hash = AddToHash(hash, mRegion.map(HashIIR).valueOr(0)); hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0)); hash = AddToHash(hash, uint8_t(mPlayback), uint32_t(mFlags)); return hash; } SurfaceKey CloneWithSize(const IntSize& aSize) const { - return SurfaceKey(aSize, mRegion, mSVGContext, mPlayback, mFlags); + return SurfaceKey(aSize, mSVGContext, mPlayback, mFlags); } const IntSize& Size() const { return mSize; } - const Maybe<ImageIntRegion>& Region() const { return mRegion; } const Maybe<SVGImageContext>& SVGContext() const { return mSVGContext; } PlaybackType Playback() const { return mPlayback; } SurfaceFlags Flags() const { return mFlags; } private: - SurfaceKey(const IntSize& aSize, const Maybe<ImageIntRegion>& aRegion, - const Maybe<SVGImageContext>& aSVGContext, PlaybackType aPlayback, - SurfaceFlags aFlags) + SurfaceKey(const IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext, + PlaybackType aPlayback, SurfaceFlags aFlags) : mSize(aSize), - mRegion(aRegion), mSVGContext(aSVGContext), mPlayback(aPlayback), mFlags(aFlags) {} - static PLDHashNumber HashIIR(const ImageIntRegion& aIIR) { - return aIIR.Hash(); - } - static PLDHashNumber HashSIC(const SVGImageContext& aSIC) { return aSIC.Hash(); } friend SurfaceKey RasterSurfaceKey(const IntSize&, SurfaceFlags, PlaybackType); friend SurfaceKey VectorSurfaceKey(const IntSize&, const Maybe<SVGImageContext>&); - friend SurfaceKey VectorSurfaceKey(const IntSize&, - const Maybe<ImageIntRegion>&, - const Maybe<SVGImageContext>&, - SurfaceFlags, PlaybackType); + friend SurfaceKey ContainerSurfaceKey( + const gfx::IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext, + SurfaceFlags aFlags); IntSize mSize; - Maybe<ImageIntRegion> mRegion; Maybe<SVGImageContext> mSVGContext; PlaybackType mPlayback; SurfaceFlags mFlags; }; inline SurfaceKey RasterSurfaceKey(const gfx::IntSize& aSize, SurfaceFlags aFlags, PlaybackType aPlayback) { - return SurfaceKey(aSize, Nothing(), Nothing(), aPlayback, aFlags); -} - -inline SurfaceKey VectorSurfaceKey(const gfx::IntSize& aSize, - const Maybe<ImageIntRegion>& aRegion, - const Maybe<SVGImageContext>& aSVGContext, - SurfaceFlags aFlags, - PlaybackType aPlayback) { - return SurfaceKey(aSize, aRegion, aSVGContext, aPlayback, aFlags); + return SurfaceKey(aSize, Nothing(), aPlayback, aFlags); } inline SurfaceKey VectorSurfaceKey(const gfx::IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext) { // We don't care about aFlags for VectorImage because none of the flags we // have right now influence VectorImage's rendering. If we add a new flag that // *does* affect how a VectorImage renders, we'll have to change this. // Similarly, we don't accept a PlaybackType parameter because we don't // currently cache frames of animated SVG images. - return SurfaceKey(aSize, Nothing(), aSVGContext, PlaybackType::eStatic, + return SurfaceKey(aSize, aSVGContext, PlaybackType::eStatic, DefaultSurfaceFlags()); } +inline SurfaceKey ContainerSurfaceKey(const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + SurfaceFlags aFlags) { + return SurfaceKey(aSize, aSVGContext, PlaybackType::eStatic, aFlags); +} + /** * AvailabilityState is used to track whether an ISurfaceProvider has a surface * available or is just a placeholder. * * To ensure that availability changes are atomic (and especially that internal * SurfaceCache code doesn't have to deal with asynchronous availability * changes), an ISurfaceProvider which starts as a placeholder can only reveal * the fact that it now has a surface available via a call to @@ -419,27 +405,16 @@ struct SurfaceCache { * it is able substitute that entry with. Note that this only applies if the * image is in factor of 2 mode. If it is not, this operation does nothing. * * @param aImageKey The image whose cache which should be pruned. */ static void PruneImage(const ImageKey aImageKey); /** - * Removes all rasterized cache entries (including placeholders) associated - * with the given image from the cache. Any blob recordings are marked as - * dirty and must be regenerated. - * - * @param aImageKey The image whose cache which should be regenerated. - * - * @returns true if any recordings were invalidated, else false. - */ - static bool InvalidateImage(const ImageKey aImageKey); - - /** * Evicts all evictable entries from the cache. * * All entries are evictable except for entries associated with locked images. * Non-evictable entries can only be removed by RemoveImage(). */ static void DiscardAll(); /**
--- a/image/SurfaceFlags.h +++ b/image/SurfaceFlags.h @@ -15,18 +15,17 @@ namespace image { /** * Flags that change the output a decoder generates. Because different * combinations of these flags result in logically different surfaces, these * flags must be taken into account in SurfaceCache lookups. */ enum class SurfaceFlags : uint8_t { NO_PREMULTIPLY_ALPHA = 1 << 0, NO_COLORSPACE_CONVERSION = 1 << 1, - TO_SRGB_COLORSPACE = 1 << 2, - RECORD_BLOB = 1 << 3, + TO_SRGB_COLORSPACE = 2 << 1, }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SurfaceFlags) /** * @return the default set of surface flags. */ inline SurfaceFlags DefaultSurfaceFlags() { return SurfaceFlags(); } @@ -40,19 +39,16 @@ inline SurfaceFlags ToSurfaceFlags(uint3 flags |= SurfaceFlags::NO_PREMULTIPLY_ALPHA; } if (aFlags & imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION) { flags |= SurfaceFlags::NO_COLORSPACE_CONVERSION; } if (aFlags & imgIContainer::FLAG_DECODE_TO_SRGB_COLORSPACE) { flags |= SurfaceFlags::TO_SRGB_COLORSPACE; } - if (aFlags & imgIContainer::FLAG_RECORD_BLOB) { - flags |= SurfaceFlags::RECORD_BLOB; - } return flags; } /** * Given a set of SurfaceFlags, returns a set of imgIContainer FLAG_* flags with * the corresponding flags set. */ inline uint32_t FromSurfaceFlags(SurfaceFlags aFlags) { @@ -61,18 +57,15 @@ inline uint32_t FromSurfaceFlags(Surface flags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; } if (aFlags & SurfaceFlags::NO_COLORSPACE_CONVERSION) { flags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; } if (aFlags & SurfaceFlags::TO_SRGB_COLORSPACE) { flags |= imgIContainer::FLAG_DECODE_TO_SRGB_COLORSPACE; } - if (aFlags & SurfaceFlags::RECORD_BLOB) { - flags |= imgIContainer::FLAG_RECORD_BLOB; - } return flags; } } // namespace image } // namespace mozilla #endif // mozilla_image_SurfaceFlags_h
--- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -32,26 +32,25 @@ #include "nsRect.h" #include "nsString.h" #include "nsStubDocumentObserver.h" #include "nsWindowSizes.h" #include "ImageRegion.h" #include "ISurfaceProvider.h" #include "LookupResult.h" #include "Orientation.h" +#include "SourceSurfaceBlobImage.h" #include "SVGDocumentWrapper.h" #include "SVGDrawingCallback.h" #include "SVGDrawingParameters.h" #include "nsIDOMEventListener.h" #include "SurfaceCache.h" -#include "BlobSurfaceProvider.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/image/Resolution.h" -#include "WindowRenderer.h" namespace mozilla { using namespace dom; using namespace dom::SVGPreserveAspectRatio_Binding; using namespace gfx; using namespace layers; @@ -367,16 +366,23 @@ size_t VectorImage::SizeOfSourceWithComp // size accurately, so we just use a constant value of 100KB, which will // generally underestimate the true size. return 100 * 1024; } return windowSizes.getTotalSize(); } +void VectorImage::CollectSizeOfSurfaces( + nsTArray<SurfaceMemoryCounter>& aCounters, + MallocSizeOf aMallocSizeOf) const { + SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf); + ImageResource::CollectSizeOfSurfaces(aCounters, aMallocSizeOf); +} + nsresult VectorImage::OnImageDataComplete(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus, bool aLastPart) { // Call our internal OnStopRequest method, which only talks to our embedded // SVG document. This won't have any effect on our ProgressTracker. nsresult finalStatus = OnStopRequest(aRequest, aStatus); // Give precedence to Necko failure codes. @@ -510,20 +516,22 @@ void VectorImage::SendInvalidationNotifi // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never // called for them. Ordinarily this isn't needed, since we send out // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the // SVG document may not be 100% ready to render at that time. In those cases // we would miss the subsequent invalidations if we didn't send out the // notifications indirectly in |InvalidateObservers...|. mHasPendingInvalidation = false; + SurfaceCache::RemoveImage(ImageKey(this)); - if (SurfaceCache::InvalidateImage(ImageKey(this))) { - // If we still have recordings in the cache, make sure we handle future - // invalidations. + if (UpdateImageContainer(Nothing())) { + // If we have image containers, that means we probably won't get a Draw call + // from the owner since they are using the container. We must assume all + // invalidations need to be handled. MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now"); mRenderingObserver->ResumeHonoringInvalidations(); } if (mProgressTracker) { mProgressTracker->SyncNotifyProgress(FLAG_FRAME_COMPLETE, GetMaxSizedIntRect()); } @@ -607,20 +615,20 @@ VectorImage::GetType(uint16_t* aType) { NS_ENSURE_ARG_POINTER(aType); *aType = imgIContainer::TYPE_VECTOR; return NS_OK; } //****************************************************************************** NS_IMETHODIMP -VectorImage::GetProviderId(uint32_t* aId) { +VectorImage::GetProducerId(uint32_t* aId) { NS_ENSURE_ARG_POINTER(aId); - *aId = ImageResource::GetImageProviderId(); + *aId = ImageResource::GetImageProducerId(); return NS_OK; } //****************************************************************************** NS_IMETHODIMP VectorImage::GetAnimated(bool* aAnimated) { if (mError || !mIsFullyLoaded) { return NS_ERROR_FAILURE; @@ -673,71 +681,135 @@ VectorImage::GetFrame(uint32_t aWhichFra } return GetFrameAtSize(imageIntSize, aWhichFrame, aFlags); } NS_IMETHODIMP_(already_AddRefed<SourceSurface>) VectorImage::GetFrameAtSize(const IntSize& aSize, uint32_t aWhichFrame, uint32_t aFlags) { - MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); - AutoProfilerImagePaintMarker PROFILER_RAII(this); #ifdef DEBUG NotifyDrawingObservers(); #endif - if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE || mError || - !mIsFullyLoaded) { - return nullptr; + auto result = + GetFrameInternal(aSize, Nothing(), Nothing(), aWhichFrame, aFlags); + return Get<2>(result).forget(); +} + +Tuple<ImgDrawResult, IntSize, RefPtr<SourceSurface>> +VectorImage::GetFrameInternal(const IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aWhichFrame, uint32_t aFlags) { + MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); + + if (aSize.IsEmpty() || aWhichFrame > FRAME_MAX_VALUE) { + return MakeTuple(ImgDrawResult::BAD_ARGS, aSize, RefPtr<SourceSurface>()); + } + + if (mError) { + return MakeTuple(ImgDrawResult::BAD_IMAGE, aSize, RefPtr<SourceSurface>()); + } + + if (!mIsFullyLoaded) { + return MakeTuple(ImgDrawResult::NOT_READY, aSize, RefPtr<SourceSurface>()); } uint32_t whichFrame = mHaveAnimations ? aWhichFrame : FRAME_FIRST; RefPtr<SourceSurface> sourceSurface; IntSize decodeSize; Tie(sourceSurface, decodeSize) = - LookupCachedSurface(aSize, Nothing(), aFlags); + LookupCachedSurface(aSize, aSVGContext, aFlags); if (sourceSurface) { - return sourceSurface.forget(); + return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, + std::move(sourceSurface)); } if (mSVGDocumentWrapper->IsDrawing()) { NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw"); - return nullptr; + return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, decodeSize, + RefPtr<SourceSurface>()); } float animTime = (whichFrame == FRAME_FIRST) ? 0.0f : mSVGDocumentWrapper->GetCurrentTimeAsFloat(); + // If we aren't given a region, create one that covers the whole SVG image. + ImageRegion region = + aRegion ? aRegion->ToImageRegion() : ImageRegion::Create(decodeSize); + // By using a null gfxContext, we ensure that we will always attempt to // create a surface, even if we aren't capable of caching it (e.g. due to our // flags, having an animation, etc). Otherwise CreateSurface will assume that // the caller is capable of drawing directly to its own draw target if we // cannot cache. - Maybe<SVGImageContext> svgContext; - SVGDrawingParameters params( - nullptr, decodeSize, aSize, ImageRegion::Create(decodeSize), - SamplingFilter::POINT, svgContext, animTime, aFlags, 1.0); + SVGDrawingParameters params(nullptr, decodeSize, aSize, region, + SamplingFilter::POINT, aSVGContext, animTime, + aFlags, 1.0); + + // Blob recorded vector images just create a simple surface responsible for + // generating blob keys and recording bindings. The recording won't happen + // until the caller requests the key after GetImageContainerAtSize. + if (aFlags & FLAG_RECORD_BLOB) { + RefPtr<SourceSurface> surface = + new SourceSurfaceBlobImage(mSVGDocumentWrapper, aSVGContext, aRegion, + decodeSize, whichFrame, aFlags); + + return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, std::move(surface)); + } bool didCache; // Was the surface put into the cache? + bool contextPaint = aSVGContext && aSVGContext->GetContextPaint(); - AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper, - /* aContextPaint */ false); + AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper, contextPaint); RefPtr<gfxDrawable> svgDrawable = CreateSVGDrawable(params); RefPtr<SourceSurface> surface = CreateSurface(params, svgDrawable, didCache); if (!surface) { MOZ_ASSERT(!didCache); - return nullptr; + return MakeTuple(ImgDrawResult::TEMPORARY_ERROR, decodeSize, + RefPtr<SourceSurface>()); } SendFrameComplete(didCache, params.flags); - return surface.forget(); + return MakeTuple(ImgDrawResult::SUCCESS, decodeSize, std::move(surface)); +} + +//****************************************************************************** +Tuple<ImgDrawResult, IntSize> VectorImage::GetImageContainerSize( + WindowRenderer* aRenderer, const IntSize& aSize, uint32_t aFlags) { + if (mError) { + return MakeTuple(ImgDrawResult::BAD_IMAGE, IntSize(0, 0)); + } + + if (!mIsFullyLoaded) { + return MakeTuple(ImgDrawResult::NOT_READY, IntSize(0, 0)); + } + + if (mHaveAnimations && !StaticPrefs::image_svg_blob_image()) { + // We don't support rasterizing animation SVGs. We can put them in a blob + // recording however instead of using fallback. + return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0)); + } + + if (aRenderer->GetBackendType() != LayersBackend::LAYERS_WR) { + return MakeTuple(ImgDrawResult::NOT_SUPPORTED, IntSize(0, 0)); + } + + // We don't need to check if the size is too big since we only support + // WebRender backends. + if (aSize.IsEmpty()) { + return MakeTuple(ImgDrawResult::BAD_ARGS, IntSize(0, 0)); + } + + return MakeTuple(ImgDrawResult::SUCCESS, aSize); } NS_IMETHODIMP_(bool) VectorImage::IsImageContainerAvailable(WindowRenderer* aRenderer, uint32_t aFlags) { if (mError || !mIsFullyLoaded || aRenderer->GetBackendType() != LayersBackend::LAYERS_WR) { return false; @@ -749,165 +821,40 @@ VectorImage::IsImageContainerAvailable(W return false; } return true; } //****************************************************************************** NS_IMETHODIMP_(ImgDrawResult) -VectorImage::GetImageProvider(WindowRenderer* aRenderer, - const gfx::IntSize& aSize, - const Maybe<SVGImageContext>& aSVGContext, - const Maybe<ImageIntRegion>& aRegion, - uint32_t aFlags, - WebRenderImageProvider** aProvider) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aRenderer); - MOZ_ASSERT(!(aFlags & FLAG_BYPASS_SURFACE_CACHE), "Unsupported flags"); - - // We don't need to check if the size is too big since we only support - // WebRender backends. - if (aSize.IsEmpty()) { - return ImgDrawResult::BAD_ARGS; - } - - if (mError) { - return ImgDrawResult::BAD_IMAGE; - } - - if (!mIsFullyLoaded) { - return ImgDrawResult::NOT_READY; - } - - if (mHaveAnimations && !(aFlags & FLAG_RECORD_BLOB)) { - // We don't support rasterizing animation SVGs. We can put them in a blob - // recording however instead of using fallback. - return ImgDrawResult::NOT_SUPPORTED; - } +VectorImage::GetImageContainerAtSize(WindowRenderer* aRenderer, + const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, + uint32_t aFlags, + layers::ImageContainer** aOutContainer) { + Maybe<SVGImageContext> newSVGContext; + MaybeRestrictSVGContext(newSVGContext, aSVGContext, aFlags); - // Only blob recordings support a region to restrict drawing. - const bool blobRecording = aFlags & FLAG_RECORD_BLOB; - MOZ_ASSERT_IF(!blobRecording, aRegion.isNothing()); - - LookupResult result(MatchType::NOT_FOUND); - auto playbackType = - mHaveAnimations ? PlaybackType::eAnimated : PlaybackType::eStatic; - auto surfaceFlags = ToSurfaceFlags(aFlags); + // The aspect ratio flag was already handled as part of the SVG context + // restriction above. + uint32_t flags = aFlags & ~(FLAG_FORCE_PRESERVEASPECTRATIO_NONE); + auto rv = GetImageContainerImpl(aRenderer, aSize, + newSVGContext ? newSVGContext : aSVGContext, + aRegion, flags, aOutContainer); - SurfaceKey surfaceKey = - VectorSurfaceKey(aSize, aRegion, aSVGContext, surfaceFlags, playbackType); - if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) { - result = SurfaceCache::Lookup(ImageKey(this), surfaceKey, - /* aMarkUsed = */ true); - } else { - result = SurfaceCache::LookupBestMatch(ImageKey(this), surfaceKey, - /* aMarkUsed = */ true); - } - - // Unless we get a best match (exact or factor of 2 limited), then we want to - // generate a new recording/rerasterize, even if we have a substitute. - if (result && (result.Type() == MatchType::EXACT || - result.Type() == MatchType::SUBSTITUTE_BECAUSE_BEST)) { - result.Surface().TakeProvider(aProvider); - return ImgDrawResult::SUCCESS; - } - - // Ensure we store the surface with the correct key if we switched to factor - // of 2 sizing or we otherwise got clamped. - IntSize rasterSize(aSize); - if (!result.SuggestedSize().IsEmpty()) { - rasterSize = result.SuggestedSize(); - surfaceKey = surfaceKey.CloneWithSize(rasterSize); + // Invalidations may still be suspended if we had a refresh tick and there + // were no image containers remaining. If we created a new container, we + // should resume invalidations to allow animations to progress. + if (*aOutContainer && mRenderingObserver) { + mRenderingObserver->ResumeHonoringInvalidations(); } - // We're about to rerasterize, which may mean that some of the previous - // surfaces we've rasterized aren't useful anymore. We can allow them to - // expire from the cache by unlocking them here, and then sending out an - // invalidation. If this image is locked, any surfaces that are still useful - // will become locked again when Draw touches them, and the remainder will - // eventually expire. - bool mayCache = SurfaceCache::CanHold(rasterSize); - if (mayCache) { - SurfaceCache::UnlockEntries(ImageKey(this)); - } - - // Blob recorded vector images just create a provider responsible for - // generating blob keys and recording bindings. The recording won't happen - // until the caller requests the key explicitly. - RefPtr<ISurfaceProvider> provider; - if (blobRecording) { - provider = MakeRefPtr<BlobSurfaceProvider>(ImageKey(this), surfaceKey, - mSVGDocumentWrapper, aFlags); - } else { - if (mSVGDocumentWrapper->IsDrawing()) { - NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw"); - return ImgDrawResult::TEMPORARY_ERROR; - } - - // We aren't using blobs, so we need to rasterize. - float animTime = - mHaveAnimations ? mSVGDocumentWrapper->GetCurrentTimeAsFloat() : 0.0f; - - // By using a null gfxContext, we ensure that we will always attempt to - // create a surface, even if we aren't capable of caching it (e.g. due to - // our flags, having an animation, etc). Otherwise CreateSurface will assume - // that the caller is capable of drawing directly to its own draw target if - // we cannot cache. - SVGDrawingParameters params( - nullptr, rasterSize, aSize, ImageRegion::Create(rasterSize), - SamplingFilter::POINT, aSVGContext, animTime, aFlags, 1.0); - - RefPtr<gfxDrawable> svgDrawable = CreateSVGDrawable(params); - bool contextPaint = aSVGContext && aSVGContext->GetContextPaint(); - AutoRestoreSVGState autoRestore(params, mSVGDocumentWrapper, contextPaint); - - mSVGDocumentWrapper->UpdateViewportBounds(params.viewportSize); - mSVGDocumentWrapper->FlushImageTransformInvalidation(); - - // Given we have no context, the default backend is fine. - BackendType backend = - gfxPlatform::GetPlatform()->GetDefaultContentBackend(); - - // Try to create an imgFrame, initializing the surface it contains by - // drawing our gfxDrawable into it. (We use FILTER_NEAREST since we never - // scale here.) - auto frame = MakeNotNull<RefPtr<imgFrame>>(); - nsresult rv = frame->InitWithDrawable( - svgDrawable, params.size, SurfaceFormat::OS_RGBA, SamplingFilter::POINT, - params.flags, backend); - - // If we couldn't create the frame, it was probably because it would end - // up way too big. Generally it also wouldn't fit in the cache, but the - // prefs could be set such that the cache isn't the limiting factor. - if (NS_FAILED(rv)) { - return ImgDrawResult::TEMPORARY_ERROR; - } - - provider = - MakeRefPtr<SimpleSurfaceProvider>(ImageKey(this), surfaceKey, frame); - } - - if (mayCache) { - // Attempt to cache the frame. - if (SurfaceCache::Insert(WrapNotNull(provider)) == InsertOutcome::SUCCESS) { - if (rasterSize != aSize) { - // We created a new surface that wasn't the size we requested, which - // means we entered factor-of-2 mode. We should purge any surfaces we - // no longer need rather than waiting for the cache to expire them. - SurfaceCache::PruneImage(ImageKey(this)); - } - - SendFrameComplete(/* aDidCache */ true, aFlags); - } - } - - MOZ_ASSERT(provider); - provider.forget(aProvider); - return ImgDrawResult::SUCCESS; + return rv; } bool VectorImage::MaybeRestrictSVGContext( Maybe<SVGImageContext>& aNewSVGContext, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags) { bool overridePAR = (aFlags & FLAG_FORCE_PRESERVEASPECTRATIO_NONE) && aSVGContext;
--- a/image/VectorImage.h +++ b/image/VectorImage.h @@ -36,16 +36,18 @@ class VectorImage final : public ImageRe // (no public constructor - use ImageFactory) // Methods inherited from Image void MediaFeatureValuesChangedAllDocuments(const MediaFeatureChange&) final; nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override; size_t GetNativeSizesLength() const override; virtual size_t SizeOfSourceWithComputedFallback( SizeOfState& aState) const override; + virtual void CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters, + MallocSizeOf aMallocSizeOf) const override; virtual nsresult OnImageDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aInStr, uint64_t aSourceOffset, uint32_t aCount) override; virtual nsresult OnImageDataComplete(nsIRequest* aRequest, nsISupports* aContext, nsresult aResult, @@ -76,16 +78,26 @@ class VectorImage final : public ImageRe virtual nsresult StartAnimation() override; virtual nsresult StopAnimation() override; virtual bool ShouldAnimate() override; private: friend class SourceSurfaceBlobImage; + Tuple<ImgDrawResult, gfx::IntSize, RefPtr<gfx::SourceSurface>> + GetFrameInternal(const gfx::IntSize& aSize, + const Maybe<SVGImageContext>& aSVGContext, + const Maybe<ImageIntRegion>& aRegion, uint32_t aWhichFrame, + uint32_t aFlags) override; + + Tuple<ImgDrawResult, gfx::IntSize> GetImageContainerSize( + WindowRenderer* aRenderer, const gfx::IntSize& aSize, + uint32_t aFlags) override; + /** * Attempt to find a matching cached surface in the SurfaceCache. Returns the * cached surface, if found, and the size to rasterize at, if applicable. * If we cannot rasterize, it will be the requested size to draw at (aSize). */ Tuple<RefPtr<gfx::SourceSurface>, gfx::IntSize> LookupCachedSurface( const gfx::IntSize& aSize, const Maybe<SVGImageContext>& aSVGContext, uint32_t aFlags);
deleted file mode 100644 --- a/image/WebRenderImageProvider.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -*- 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/. */ - -#ifndef MOZILLA_IMAGE_WEBRENDERIMAGEPROVIDER_H_ -#define MOZILLA_IMAGE_WEBRENDERIMAGEPROVIDER_H_ - -#include "nsISupportsImpl.h" - -namespace mozilla { -namespace layers { -class RenderRootStateManager; -} - -namespace wr { -class IpcResourceUpdateQueue; -struct ExternalImageId; -struct ImageKey; -} // namespace wr - -namespace image { - -class ImageResource; -using ImageProviderId = uint32_t; - -class WebRenderImageProvider { - public: - // Subclasses may or may not be XPCOM classes, so we just require that they - // implement AddRef and Release. - NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING - - ImageProviderId GetProviderId() const { return mProviderId; } - - static ImageProviderId AllocateProviderId(); - - /** - * Generate an ImageKey for the given frame. - * @param aSurface The current frame. This should match what was cached via - * SetCurrentFrame, but if it does not, it will need to - * regenerate the cached ImageKey. - */ - virtual nsresult UpdateKey(layers::RenderRootStateManager* aManager, - wr::IpcResourceUpdateQueue& aResources, - wr::ImageKey& aKey) { - return NS_ERROR_NOT_AVAILABLE; - } - - /** - * Invalidate if a blob recording, requiring it to be regenerated. - */ - virtual void InvalidateRecording() {} - - protected: - WebRenderImageProvider(const ImageResource* aImage); - - private: - ImageProviderId mProviderId; -}; - -} // namespace image -} // namespace mozilla - -#endif /* MOZILLA_IMAGE_WEBRENDERIMAGEPROVIDER_H_ */
--- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -675,16 +675,17 @@ bool imgFrame::AreAllPixelsWritten() con return mDecoded.IsEqualInterior(GetRect()); } void imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) const { MonitorAutoLock lock(mMonitor); AddSizeOfCbData metadata; + metadata.mSurface = mOptSurface ? mOptSurface.get() : mRawSurface.get(); metadata.mFinished = mFinished; if (mOptSurface) { metadata.mHeapBytes += aMallocSizeOf(mOptSurface); SourceSurface::SizeOfInfo info; mOptSurface->SizeOfExcludingThis(aMallocSizeOf, info); metadata.Accumulate(info);
--- a/image/imgFrame.h +++ b/image/imgFrame.h @@ -163,16 +163,17 @@ class imgFrame { void FinalizeSurface(); already_AddRefed<SourceSurface> GetSourceSurface(); struct AddSizeOfCbData : public SourceSurface::SizeOfInfo { AddSizeOfCbData() : SourceSurface::SizeOfInfo(), mIndex(0), mFinished(false) {} + const gfx::SourceSurface* mSurface; size_t mIndex; bool mFinished; }; typedef std::function<void(AddSizeOfCbData& aMetadata)> AddSizeOfCb; void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, const AddSizeOfCb& aCallback) const;
--- a/image/imgIContainer.idl +++ b/image/imgIContainer.idl @@ -43,17 +43,16 @@ class SVGImageContext; struct MediaFeatureChange; } namespace mozilla { namespace image { class ImageRegion; class ImageIntRegion; -class WebRenderImageProvider; struct Orientation; struct Resolution; } } %} @@ -66,17 +65,16 @@ native ImgDrawResult(mozilla::image::Img native SamplingFilter(mozilla::gfx::SamplingFilter); [ref] native nsIntRect(nsIntRect); native nsIntRectByVal(nsIntRect); [ref] native nsIntSize(nsIntSize); native nsSize(nsSize); [ptr] native nsIFrame(nsIFrame); native TempRefImageContainer(already_AddRefed<mozilla::layers::ImageContainer>); [ptr] native ImageContainer(mozilla::layers::ImageContainer); -[ptr] native WebRenderImageProvider(mozilla::image::WebRenderImageProvider); [ref] native ImageRegion(mozilla::image::ImageRegion); [ptr] native WindowRenderer(mozilla::WindowRenderer); native Orientation(mozilla::image::Orientation); native ImageResolution(mozilla::image::Resolution); [ref] native TimeStamp(mozilla::TimeStamp); [ref] native MaybeSVGImageContext(mozilla::Maybe<mozilla::SVGImageContext>); [ref] native MaybeImageIntRegion(mozilla::Maybe<mozilla::image::ImageIntRegion>); native TempRefSourceSurface(already_AddRefed<mozilla::gfx::SourceSurface>); @@ -165,19 +163,19 @@ interface imgIContainer : nsISupports * Whether this image is animated. You can only be guaranteed that querying * this will not throw if STATUS_DECODE_COMPLETE is set on the imgIRequest. * * @throws NS_ERROR_NOT_AVAILABLE if the animated state cannot be determined. */ readonly attribute boolean animated; /** - * Provider ID for image providers created by this image. + * Producer ID for image containers created by this image. */ - [infallible] readonly attribute unsigned long providerId; + [infallible] readonly attribute unsigned long producerId; /** * Flags for imgIContainer operations. * * Meanings: * * FLAG_NONE: Lack of flags. * @@ -321,41 +319,43 @@ interface imgIContainer : nsISupports * @return true if getImageContainer() is expected to return a valid * ImageContainer when passed the given @Renderer and @Flags * parameters. */ [noscript, notxpcom] boolean isImageContainerAvailable(in WindowRenderer aRenderer, in uint32_t aFlags); /** - * Attempts to find a WebRenderImageProvider containing the current frame at - * the given size. Match the requested size is best effort; it's not - * guaranteed that the surface you get will be a perfect match. (Some reasons - * you may get a surface of a different size include: if you requested - * upscaling, or if downscale-during-decode is disabled.) + * Attempts to create an ImageContainer (and Image) containing the current + * frame at the given size. Match the requested size is best effort; it's + * not guaranteed that the surface you get will be a perfect match. (Some + * reasons you may get a surface of a different size include: if you + * requested upscaling, or if downscale-during-decode is disabled.) + * + * Avoid calling this unless you're actually going to layerize this image. * * @param aRenderer The WindowRenderer which will be used to render the * ImageContainer. * @param aSVGContext If specified, SVG-related rendering context, such as * overridden attributes on the image document's root <svg> * node, and the size of the viewport that the full image * would occupy. Ignored for raster images. * @param aFlags Decoding / drawing flags (in other words, FLAG_* flags). * Currently only FLAG_SYNC_DECODE and FLAG_SYNC_DECODE_IF_FAST * are supported. - * @param aProvider Return value for WebRenderImageProvider for the current - * frame. May be null depending on the draw result. + * @param aContainer Return value for ImageContainer for the current frame. + * May be null depending on the draw result. * @return The draw result for the current frame. */ - [noscript, notxpcom] ImgDrawResult getImageProvider(in WindowRenderer aRenderer, - [const] in nsIntSize aSize, - [const] in MaybeSVGImageContext aSVGContext, - [const] in MaybeImageIntRegion aRegion, - in uint32_t aFlags, - out WebRenderImageProvider aProvider); + [noscript, notxpcom] ImgDrawResult getImageContainerAtSize(in WindowRenderer aRenderer, + [const] in nsIntSize aSize, + [const] in MaybeSVGImageContext aSVGContext, + [const] in MaybeImageIntRegion aRegion, + in uint32_t aFlags, + out ImageContainer aOutContainer); /** * Draw the requested frame of this image onto the context specified. * * Drawing an image involves scaling it to a certain size (which may be * implemented as a "smart" scale by substituting an HQ-scaled frame or * rendering at a high DPI), and then selecting a region of that image to * draw. That region is drawn onto the graphics context and in the process
--- a/image/imgIRequest.idl +++ b/image/imgIRequest.idl @@ -26,19 +26,19 @@ interface imgIRequest : nsIRequest /** * the image container... * @return the image object associated with the request. * @attention NEED DOCS */ readonly attribute imgIContainer image; /** - * Provider ID for image providers created by this image. + * Producer ID for image containers created by this image. */ - [infallible] readonly attribute unsigned long providerId; + [infallible] readonly attribute unsigned long producerId; /** * Bits set in the return value from imageStatus * @name statusflags * * Meanings: * * STATUS_NONE: Nothing to report.
--- a/image/imgLoader.cpp +++ b/image/imgLoader.cpp @@ -394,49 +394,16 @@ class imgMemoryReporter final : public n } if (counter.Key().Flags() != DefaultSurfaceFlags()) { surfacePathPrefix.AppendLiteral(", flags:"); surfacePathPrefix.AppendInt(uint32_t(counter.Key().Flags()), /* aRadix = */ 16); } - if (counter.Key().Region()) { - const ImageIntRegion& region = counter.Key().Region().ref(); - const gfx::IntRect& rect = region.Rect(); - surfacePathPrefix.AppendLiteral(", region:[ rect=("); - surfacePathPrefix.AppendInt(rect.x); - surfacePathPrefix.AppendLiteral(","); - surfacePathPrefix.AppendInt(rect.y); - surfacePathPrefix.AppendLiteral(") "); - surfacePathPrefix.AppendInt(rect.width); - surfacePathPrefix.AppendLiteral("x"); - surfacePathPrefix.AppendInt(rect.height); - if (region.IsRestricted()) { - const gfx::IntRect& restrict = region.Restriction(); - if (restrict == rect) { - surfacePathPrefix.AppendLiteral(", restrict=rect"); - } else { - surfacePathPrefix.AppendLiteral(", restrict=("); - surfacePathPrefix.AppendInt(restrict.x); - surfacePathPrefix.AppendLiteral(","); - surfacePathPrefix.AppendInt(restrict.y); - surfacePathPrefix.AppendLiteral(") "); - surfacePathPrefix.AppendInt(restrict.width); - surfacePathPrefix.AppendLiteral("x"); - surfacePathPrefix.AppendInt(restrict.height); - } - } - if (region.GetExtendMode() != gfx::ExtendMode::CLAMP) { - surfacePathPrefix.AppendLiteral(", extendMode="); - surfacePathPrefix.AppendInt(int32_t(region.GetExtendMode())); - } - surfacePathPrefix.AppendLiteral("]"); - } - if (counter.Key().SVGContext()) { const SVGImageContext& context = counter.Key().SVGContext().ref(); surfacePathPrefix.AppendLiteral(", svgContext:[ "); if (context.GetViewportSize()) { const CSSIntSize& size = context.GetViewportSize().ref(); surfacePathPrefix.AppendLiteral("viewport=("); surfacePathPrefix.AppendInt(size.width); surfacePathPrefix.AppendLiteral("x");
--- a/image/imgRequestProxy.cpp +++ b/image/imgRequestProxy.cpp @@ -662,25 +662,25 @@ imgRequestProxy::GetImage(imgIContainer* } imageToReturn.swap(*aImage); return NS_OK; } NS_IMETHODIMP -imgRequestProxy::GetProviderId(uint32_t* aId) { +imgRequestProxy::GetProducerId(uint32_t* aId) { NS_ENSURE_TRUE(aId, NS_ERROR_NULL_POINTER); nsCOMPtr<imgIContainer> image; nsresult rv = GetImage(getter_AddRefs(image)); if (NS_SUCCEEDED(rv)) { - *aId = image->GetProviderId(); + *aId = image->GetProducerId(); } else { - *aId = 0; + *aId = layers::kContainerProducerID_Invalid; } return NS_OK; } NS_IMETHODIMP imgRequestProxy::GetImageStatus(uint32_t* aStatus) { if (IsValidating()) {
--- a/image/moz.build +++ b/image/moz.build @@ -60,23 +60,22 @@ EXPORTS += [ EXPORTS.mozilla.image += [ "encoders/bmp/nsBMPEncoder.h", "encoders/ico/nsICOEncoder.h", "encoders/jpeg/nsJPEGEncoder.h", "encoders/png/nsPNGEncoder.h", "ICOFileHeaders.h", "ImageMemoryReporter.h", "Resolution.h", - "WebRenderImageProvider.h", + "SourceSurfaceBlobImage.h", ] UNIFIED_SOURCES += [ "AnimationFrameBuffer.cpp", "AnimationSurfaceProvider.cpp", - "BlobSurfaceProvider.cpp", "ClippedImage.cpp", "DecodedSurfaceProvider.cpp", "Decoder.cpp", "DecoderFactory.cpp", "DynamicImage.cpp", "FrameAnimator.cpp", "FrozenImage.cpp", "IDecodingTask.cpp", @@ -94,16 +93,17 @@ UNIFIED_SOURCES += [ "imgTools.cpp", "MultipartImage.cpp", "OrientedImage.cpp", "ProgressTracker.cpp", "RasterImage.cpp", "ScriptedNotificationObserver.cpp", "ShutdownTracker.cpp", "SourceBuffer.cpp", + "SourceSurfaceBlobImage.cpp", "SurfaceCache.cpp", "SurfaceCacheUtils.cpp", "SurfacePipe.cpp", "SVGDocumentWrapper.cpp", "VectorImage.cpp", ] UNIFIED_SOURCES += ["Downscaler.cpp"]
new file mode 100644 --- /dev/null +++ b/image/test/gtest/TestContainers.cpp @@ -0,0 +1,107 @@ +/* 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 "gtest/gtest.h" + +#include "WindowRenderer.h" +#include "Common.h" +#include "imgIContainer.h" +#include "ImageFactory.h" +#include "ImageContainer.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsIInputStream.h" +#include "nsString.h" +#include "ProgressTracker.h" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::image; + +class ImageContainers : public ::testing::Test { + protected: + AutoInitializeImageLib mInit; +}; + +TEST_F(ImageContainers, RasterImageContainer) { + ImageTestCase testCase = GreenPNGTestCase(); + + // Create an image. + RefPtr<Image> image = ImageFactory::CreateAnonymousImage( + nsDependentCString(testCase.mMimeType)); + ASSERT_TRUE(!image->HasError()); + + nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); + ASSERT_TRUE(inputStream); + + // Figure out how much data we have. + uint64_t length; + nsresult rv = inputStream->Available(&length); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + // Write the data into the image. + rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0, + static_cast<uint32_t>(length)); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + // Let the image know we've sent all the data. + rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); + tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); + + RefPtr<WindowRenderer> renderer = new FallbackRenderer; + + // Get at native size. + ImgDrawResult drawResult; + RefPtr<layers::ImageContainer> nativeContainer; + drawResult = image->GetImageContainerAtSize( + renderer, testCase.mSize, Nothing(), Nothing(), + imgIContainer::FLAG_SYNC_DECODE, getter_AddRefs(nativeContainer)); + EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS); + ASSERT_TRUE(nativeContainer != nullptr); + IntSize containerSize = nativeContainer->GetCurrentSize(); + EXPECT_EQ(testCase.mSize.width, containerSize.width); + EXPECT_EQ(testCase.mSize.height, containerSize.height); + + // Upscaling should give the native size. + IntSize requestedSize = testCase.mSize; + requestedSize.Scale(2, 2); + RefPtr<layers::ImageContainer> upscaleContainer; + drawResult = image->GetImageContainerAtSize( + renderer, requestedSize, Nothing(), Nothing(), + imgIContainer::FLAG_SYNC_DECODE | + imgIContainer::FLAG_HIGH_QUALITY_SCALING, + getter_AddRefs(upscaleContainer)); + EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS); + ASSERT_TRUE(upscaleContainer != nullptr); + containerSize = upscaleContainer->GetCurrentSize(); + EXPECT_EQ(testCase.mSize.width, containerSize.width); + EXPECT_EQ(testCase.mSize.height, containerSize.height); + + // Downscaling should give the downscaled size. + requestedSize = testCase.mSize; + requestedSize.width /= 2; + requestedSize.height /= 2; + RefPtr<layers::ImageContainer> downscaleContainer; + drawResult = image->GetImageContainerAtSize( + renderer, requestedSize, Nothing(), Nothing(), + imgIContainer::FLAG_SYNC_DECODE | + imgIContainer::FLAG_HIGH_QUALITY_SCALING, + getter_AddRefs(downscaleContainer)); + EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS); + ASSERT_TRUE(downscaleContainer != nullptr); + containerSize = downscaleContainer->GetCurrentSize(); + EXPECT_EQ(requestedSize.width, containerSize.width); + EXPECT_EQ(requestedSize.height, containerSize.height); + + // Get at native size again. Should give same container. + RefPtr<layers::ImageContainer> againContainer; + drawResult = image->GetImageContainerAtSize( + renderer, testCase.mSize, Nothing(), Nothing(), + imgIContainer::FLAG_SYNC_DECODE, getter_AddRefs(againContainer)); + EXPECT_EQ(drawResult, ImgDrawResult::SUCCESS); + ASSERT_EQ(nativeContainer.get(), againContainer.get()); +}
--- a/image/test/gtest/moz.build +++ b/image/test/gtest/moz.build @@ -6,16 +6,17 @@ Library("imagetest") UNIFIED_SOURCES = [ "Common.cpp", "TestADAM7InterpolatingFilter.cpp", "TestAnimationFrameBuffer.cpp", "TestBlendAnimationFilter.cpp", + "TestContainers.cpp", "TestCopyOnWrite.cpp", "TestDeinterlacingFilter.cpp", "TestFrameAnimator.cpp", "TestLoader.cpp", "TestRemoveFrameRectFilter.cpp", "TestStreamingLexer.cpp", "TestSurfaceSink.cpp", "TestSwizzleFilter.cpp",
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -6603,42 +6603,36 @@ IntSize nsLayoutUtils::ComputeImageConta // ImageIntRegion for vector images, for which we may only draw part of in // some cases. if (aImage->GetType() != imgIContainer::TYPE_VECTOR) { return aImage->OptimalImageSizeForDest( gfxSize(destRect.Width(), destRect.Height()), imgIContainer::FRAME_CURRENT, samplingFilter, aFlags); } - // We only use the region rect with blob recordings. This is because when we - // rasterize an SVG image in process, we always create a complete - // rasterization of the whole image which can be given to any caller, while - // we support partial rasterization with the blob recordings. - if (aFlags & imgIContainer::FLAG_RECORD_BLOB) { - // If the dest rect contains the fill rect, then we are only displaying part - // of the vector image. We need to calculate the restriction region to avoid - // drawing more than we need, and sampling outside the desired bounds. - LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect); - if (destRect.Contains(clipRect)) { - LayerIntRect restrictRect = destRect.Intersect(clipRect); - restrictRect.MoveBy(-destRect.TopLeft()); - - if (restrictRect.Width() < 1) { - restrictRect.SetWidth(1); - } - if (restrictRect.Height() < 1) { - restrictRect.SetHeight(1); - } - - if (restrictRect.X() != 0 || restrictRect.Y() != 0 || - restrictRect.Size() != destRect.Size()) { - IntRect sampleRect = restrictRect.ToUnknownRect(); - aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction( - sampleRect, sampleRect, ExtendMode::CLAMP)); - } + // If the dest rect contains the fill rect, then we are only displaying part + // of the vector image. We need to calculate the restriction region to avoid + // drawing more than we need, and sampling outside the desired bounds. + LayerIntRect clipRect = SnapRectForImage(itm, scaleFactors, aFillRect); + if (destRect.Contains(clipRect)) { + LayerIntRect restrictRect = destRect.Intersect(clipRect); + restrictRect.MoveBy(-destRect.TopLeft()); + + if (restrictRect.Width() < 1) { + restrictRect.SetWidth(1); + } + if (restrictRect.Height() < 1) { + restrictRect.SetHeight(1); + } + + if (restrictRect.X() != 0 || restrictRect.Y() != 0 || + restrictRect.Size() != destRect.Size()) { + IntRect sampleRect = restrictRect.ToUnknownRect(); + aRegion = Some(ImageIntRegion::CreateWithSamplingRestriction( + sampleRect, sampleRect, ExtendMode::CLAMP)); } } // VectorImage::OptimalImageSizeForDest will just round up, but we already // have an integer size. return destRect.Size().ToUnknownSize(); }
--- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -21,17 +21,16 @@ #include "mozilla/dom/ImageTracker.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Helpers.h" #include "mozilla/gfx/PathHelpers.h" #include "mozilla/dom/GeneratedImageContent.h" #include "mozilla/dom/HTMLAreaElement.h" #include "mozilla/dom/HTMLImageElement.h" #include "mozilla/dom/ResponsiveImageSelector.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/MouseEvents.h" #include "mozilla/PresShell.h" #include "mozilla/PresShellInlines.h" #include "mozilla/StaticPrefs_image.h" #include "mozilla/StaticPrefs_layout.h" #include "mozilla/SVGImageContext.h" @@ -994,18 +993,19 @@ void nsImageFrame::OnFrameUpdate(imgIReq InvalidateSelf(&layerInvalidRect, &frameInvalidRect); } void nsImageFrame::InvalidateSelf(const nsIntRect* aLayerInvalidRect, const nsRect* aFrameInvalidRect) { // Check if WebRender has interacted with this frame. If it has // we need to let it know that things have changed. const auto type = DisplayItemType::TYPE_IMAGE; - const auto providerId = mImage ? mImage->GetProviderId() : 0; - if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) { + const auto producerId = + mImage ? mImage->GetProducerId() : kContainerProducerID_Invalid; + if (WebRenderUserData::ProcessInvalidateForImage(this, type, producerId)) { return; } InvalidateLayer(type, aLayerInvalidRect, aFrameInvalidRect); if (!mFirstFrameComplete) { InvalidateLayer(DisplayItemType::TYPE_ALT_FEEDBACK, aLayerInvalidRect, aFrameInvalidRect); @@ -1838,23 +1838,23 @@ ImgDrawResult nsImageFrame::DisplayAltFe LayoutDeviceRect destRect(LayoutDeviceRect::FromAppUnits(dest, factor)); Maybe<SVGImageContext> svgContext; Maybe<ImageIntRegion> region; IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( imgCon, this, destRect, destRect, aSc, aFlags, svgContext, region); - RefPtr<image::WebRenderImageProvider> provider; - result = imgCon->GetImageProvider(aManager->LayerManager(), decodeSize, - svgContext, region, aFlags, - getter_AddRefs(provider)); - if (provider) { - bool wrResult = aManager->CommandBuilder().PushImageProvider( - aItem, provider, aBuilder, aResources, destRect, bounds); + RefPtr<ImageContainer> container; + result = imgCon->GetImageContainerAtSize( + aManager->LayerManager(), decodeSize, svgContext, region, aFlags, + getter_AddRefs(container)); + if (container) { + bool wrResult = aManager->CommandBuilder().PushImage( + aItem, container, aBuilder, aResources, aSc, destRect, bounds); result &= wrResult ? ImgDrawResult::SUCCESS : ImgDrawResult::NOT_READY; } else { // We don't use &= here because we want the result to be NOT_READY so // the next block executes. result = ImgDrawResult::NOT_READY; } } @@ -2093,20 +2093,20 @@ bool nsDisplayImage::CreateWebRenderComm LayoutDeviceRect destRect( LayoutDeviceRect::FromAppUnits(GetDestRect(), factor)); Maybe<SVGImageContext> svgContext; Maybe<ImageIntRegion> region; IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( mImage, mFrame, destRect, destRect, aSc, flags, svgContext, region); - RefPtr<image::WebRenderImageProvider> provider; - ImgDrawResult drawResult = - mImage->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext, - region, flags, getter_AddRefs(provider)); + RefPtr<layers::ImageContainer> container; + ImgDrawResult drawResult = mImage->GetImageContainerAtSize( + aManager->LayerManager(), decodeSize, svgContext, region, flags, + getter_AddRefs(container)); // While we got a container, it may not contain a fully decoded surface. If // that is the case, and we have an image we were previously displaying which // has a fully decoded surface, then we should prefer the previous image. bool updatePrevImage = false; switch (drawResult) { case ImgDrawResult::NOT_READY: case ImgDrawResult::INCOMPLETE: @@ -2118,23 +2118,23 @@ bool nsDisplayImage::CreateWebRenderComm uint32_t prevFlags = flags; if (StaticPrefs::image_svg_blob_image() && mPrevImage->GetType() == imgIContainer::TYPE_VECTOR) { prevFlags |= imgIContainer::FLAG_RECORD_BLOB; } else { prevFlags &= ~imgIContainer::FLAG_RECORD_BLOB; } - RefPtr<image::WebRenderImageProvider> prevProvider; - ImgDrawResult newDrawResult = mPrevImage->GetImageProvider( + RefPtr<ImageContainer> prevContainer; + ImgDrawResult newDrawResult = mPrevImage->GetImageContainerAtSize( aManager->LayerManager(), decodeSize, svgContext, region, prevFlags, - getter_AddRefs(prevProvider)); - if (prevProvider && newDrawResult == ImgDrawResult::SUCCESS) { + getter_AddRefs(prevContainer)); + if (prevContainer && newDrawResult == ImgDrawResult::SUCCESS) { drawResult = newDrawResult; - provider = std::move(prevProvider); + container = std::move(prevContainer); flags = prevFlags; break; } // Previous image was unusable; we can forget about it. updatePrevImage = true; } break; @@ -2151,19 +2151,24 @@ bool nsDisplayImage::CreateWebRenderComm if (updatePrevImage) { mPrevImage = mImage; frame->mPrevImage = frame->mImage; } // If the image container is empty, we don't want to fallback. Any other // failure will be due to resource constraints and fallback is unlikely to // help us. Hence we can ignore the return value from PushImage. - if (provider) { - aManager->CommandBuilder().PushImageProvider( - this, provider, aBuilder, aResources, destRect, destRect); + if (container) { + if (flags & imgIContainer::FLAG_RECORD_BLOB) { + aManager->CommandBuilder().PushBlobImage(this, container, aBuilder, + aResources, destRect, destRect); + } else { + aManager->CommandBuilder().PushImage(this, container, aBuilder, + aResources, aSc, destRect, destRect); + } } nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, drawResult); return true; } ImgDrawResult nsImageFrame::PaintImage(gfxContext& aRenderingContext, nsPoint aPt, const nsRect& aDirtyRect,
--- a/layout/painting/nsCSSRenderingBorders.cpp +++ b/layout/painting/nsCSSRenderingBorders.cpp @@ -24,17 +24,16 @@ #include "nsDisplayList.h" #include "nsExpirationTracker.h" #include "nsIScriptError.h" #include "nsClassHashtable.h" #include "nsPresContext.h" #include "nsStyleStruct.h" #include "gfx2DGlue.h" #include "gfxGradientCache.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/ProfilerLabels.h" #include "mozilla/Range.h" #include <algorithm> using namespace mozilla; @@ -3624,43 +3623,44 @@ ImgDrawResult nsCSSBorderImageRenderer:: Maybe<SVGImageContext> svgContext; Maybe<ImageIntRegion> region; gfx::IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( img, aForFrame, imageRect, imageRect, aSc, flags, svgContext, region); - RefPtr<WebRenderImageProvider> provider; - drawResult = img->GetImageProvider(aManager->LayerManager(), decodeSize, - svgContext, region, flags, - getter_AddRefs(provider)); - if (!provider) { + RefPtr<layers::ImageContainer> container; + drawResult = img->GetImageContainerAtSize( + aManager->LayerManager(), decodeSize, svgContext, region, flags, + getter_AddRefs(container)); + if (!container) { break; } - Maybe<wr::ImageKey> key = - aManager->CommandBuilder().CreateImageProviderKey(aItem, provider, - aResources); + auto rendering = + wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); + gfx::IntSize size; + Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( + aItem, container, aBuilder, aResources, rendering, aSc, size, + Nothing()); if (key.isNothing()) { break; } if (mFill) { float epsilon = 0.0001; bool noVerticalBorders = widths[0] <= epsilon && widths[2] < epsilon; bool noHorizontalBorders = widths[1] <= epsilon && widths[3] < epsilon; // Border image with no border. It's a little silly but WebRender // currently does not handle this. We could fall back to a blob image // but there are reftests that are sensible to the test going through a // blob while the reference doesn't. if (noVerticalBorders && noHorizontalBorders) { - auto rendering = - wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, key.value()); break; } // Fall-back if we want to fill the middle area and opposite edges are // both empty. // TODO(bug 1609893): moving some of the repetition handling code out
--- a/layout/painting/nsImageRenderer.cpp +++ b/layout/painting/nsImageRenderer.cpp @@ -9,17 +9,16 @@ #include "nsImageRenderer.h" #include "mozilla/webrender/WebRenderAPI.h" #include "gfxContext.h" #include "gfxDrawable.h" #include "ImageOps.h" #include "ImageRegion.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/StackingContextHelper.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "nsContentUtils.h" #include "nsCSSRendering.h" #include "nsCSSRenderingGradients.h" #include "nsDeviceContext.h" #include "nsIFrame.h" @@ -617,39 +616,49 @@ ImgDrawResult nsImageRenderer::BuildWebR LayoutDeviceRect::FromAppUnits(aFill, appUnitsPerDevPixel); auto stretchSize = wr::ToLayoutSize(destRect.Size()); gfx::IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( mImageContainer, mForFrame, destRect, clipRect, aSc, containerFlags, svgContext, region); - RefPtr<image::WebRenderImageProvider> provider; - drawResult = mImageContainer->GetImageProvider( + if (extendMode != ExtendMode::CLAMP) { + region = Nothing(); + } + + RefPtr<layers::ImageContainer> container; + drawResult = mImageContainer->GetImageContainerAtSize( aManager->LayerManager(), decodeSize, svgContext, region, - containerFlags, getter_AddRefs(provider)); - if (!provider) { + containerFlags, getter_AddRefs(container)); + if (!container) { NS_WARNING("Failed to get image container"); break; } - Maybe<wr::ImageKey> key = - aManager->CommandBuilder().CreateImageProviderKey(aItem, provider, - aResources); - if (key.isNothing()) { + if (containerFlags & imgIContainer::FLAG_RECORD_BLOB) { + MOZ_ASSERT(extendMode == ExtendMode::CLAMP); + aManager->CommandBuilder().PushBlobImage( + aItem, container, aBuilder, aResources, clipRect, clipRect); break; } auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); - wr::LayoutRect clip = wr::ToLayoutRect(clipRect); + gfx::IntSize size; + Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( + aItem, container, aBuilder, aResources, rendering, aSc, size, + Nothing()); - // If we provided a region to the provider, then it already took the - // dest rect into account when it did the recording. - wr::LayoutRect dest = region ? clip : wr::ToLayoutRect(destRect); + if (key.isNothing()) { + break; + } + + wr::LayoutRect dest = wr::ToLayoutRect(destRect); + wr::LayoutRect clip = wr::ToLayoutRect(clipRect); if (extendMode == ExtendMode::CLAMP) { // The image is not repeating. Just push as a regular image. aBuilder.PushImage(dest, clip, !aItem->BackfaceIsHidden(), rendering, key.value(), true, wr::ColorF{1.0f, 1.0f, 1.0f, aOpacity}); } else { nsPoint firstTilePos = nsLayoutUtils::GetBackgroundFirstTilePos(
--- a/layout/style/ImageLoader.cpp +++ b/layout/style/ImageLoader.cpp @@ -535,19 +535,19 @@ static void InvalidateImages(nsIFrame* a } // XXX: handle Blob data invalidateFrame = true; break; case layers::WebRenderUserData::UserDataType::eMask: static_cast<layers::WebRenderMaskData*>(data.get())->Invalidate(); invalidateFrame = true; break; - case layers::WebRenderUserData::UserDataType::eImageProvider: - if (static_cast<layers::WebRenderImageProviderData*>(data.get()) - ->Invalidate(aRequest->GetProviderId())) { + case layers::WebRenderUserData::UserDataType::eImage: + if (static_cast<layers::WebRenderImageData*>(data.get()) + ->UsingSharedSurface(aRequest->GetProducerId())) { break; } [[fallthrough]]; default: invalidateFrame = true; break; } }
--- a/layout/svg/SVGImageFrame.cpp +++ b/layout/svg/SVGImageFrame.cpp @@ -5,17 +5,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SVGImageFrame.h" // Keep in (case-insensitive) order: #include "gfxContext.h" #include "gfxPlatform.h" #include "mozilla/gfx/2D.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "imgIContainer.h" #include "ImageRegion.h" #include "nsContainerFrame.h" #include "nsIImageLoadingContent.h" #include "nsLayoutUtils.h" #include "imgINotificationObserver.h" @@ -616,20 +615,20 @@ bool SVGImageFrame::CreateWebRenderComma Some(imgElem->mPreserveAspectRatio.GetAnimValue())); } Maybe<ImageIntRegion> region; IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( mImageContainer, this, destRect, clipRect, aSc, flags, svgContext, region); - RefPtr<image::WebRenderImageProvider> provider; - ImgDrawResult drawResult = mImageContainer->GetImageProvider( + RefPtr<layers::ImageContainer> container; + ImgDrawResult drawResult = mImageContainer->GetImageContainerAtSize( aManager->LayerManager(), decodeSize, svgContext, region, flags, - getter_AddRefs(provider)); + getter_AddRefs(container)); // While we got a container, it may not contain a fully decoded surface. If // that is the case, and we have an image we were previously displaying which // has a fully decoded surface, then we should prefer the previous image. switch (drawResult) { case ImgDrawResult::NOT_READY: case ImgDrawResult::TEMPORARY_ERROR: // nothing to draw (yet) @@ -643,19 +642,24 @@ bool SVGImageFrame::CreateWebRenderComma } // Don't do any actual mutations to state if we're doing a dry run // (used to decide if we're making this into an active layer) if (!aDryRun) { // If the image container is empty, we don't want to fallback. Any other // failure will be due to resource constraints and fallback is unlikely to // help us. Hence we can ignore the return value from PushImage. - if (provider) { - aManager->CommandBuilder().PushImageProvider( - aItem, provider, aBuilder, aResources, destRect, clipRect); + if (container) { + if (flags & imgIContainer::FLAG_RECORD_BLOB) { + aManager->CommandBuilder().PushBlobImage( + aItem, container, aBuilder, aResources, destRect, clipRect); + } else { + aManager->CommandBuilder().PushImage( + aItem, container, aBuilder, aResources, aSc, destRect, clipRect); + } } nsDisplayItemGenericImageGeometry::UpdateDrawResult(aItem, drawResult); } return true; }
--- a/layout/xul/nsImageBoxFrame.cpp +++ b/layout/xul/nsImageBoxFrame.cpp @@ -46,17 +46,16 @@ #include "mozilla/BasicEvents.h" #include "mozilla/EventDispatcher.h" #include "mozilla/Maybe.h" #include "mozilla/PresShell.h" #include "mozilla/StaticPrefs_image.h" #include "mozilla/SVGImageContext.h" #include "Units.h" -#include "mozilla/image/WebRenderImageProvider.h" #include "mozilla/layers/RenderRootStateManager.h" #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/dom/ImageTracker.h" #if defined(XP_WIN) // Undefine LoadImage to prevent naming conflict with Windows. # undef LoadImage #endif @@ -424,35 +423,49 @@ ImgDrawResult nsImageBoxFrame::CreateWeb Maybe<SVGImageContext> svgContext; Maybe<ImageIntRegion> region; gfx::IntSize decodeSize = nsLayoutUtils::ComputeImageContainerDrawingParameters( imgCon, aItem->Frame(), fillRect, fillRect, aSc, aFlags, svgContext, region); - RefPtr<image::WebRenderImageProvider> provider; - result = - imgCon->GetImageProvider(aManager->LayerManager(), decodeSize, svgContext, - region, aFlags, getter_AddRefs(provider)); - if (!provider) { - NS_WARNING("Failed to get image provider"); + RefPtr<layers::ImageContainer> container; + result = imgCon->GetImageContainerAtSize(aManager->LayerManager(), decodeSize, + svgContext, region, aFlags, + getter_AddRefs(container)); + if (!container) { + NS_WARNING("Failed to get image container"); return result; } auto rendering = wr::ToImageRendering(aItem->Frame()->UsedImageRendering()); wr::LayoutRect fill = wr::ToLayoutRect(fillRect); - Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageProviderKey( - aItem, provider, aResources); + if (aFlags & imgIContainer::FLAG_RECORD_BLOB) { + Maybe<wr::BlobImageKey> key = aManager->CommandBuilder().CreateBlobImageKey( + aItem, container, aResources); + if (key.isNothing()) { + return result; + } + + aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering, + wr::AsImageKey(key.value())); + return result; + } + + gfx::IntSize size; + Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey( + aItem, container, aBuilder, aResources, rendering, aSc, size, Nothing()); if (key.isNothing()) { return result; } aBuilder.PushImage(fill, fill, !BackfaceIsHidden(), rendering, key.value()); + return result; } nsRect nsImageBoxFrame::GetDestRect(const nsPoint& aOffset, Maybe<nsPoint>& aAnchorPoint) { nsCOMPtr<imgIContainer> imgCon; mImageRequest->GetImage(getter_AddRefs(imgCon)); MOZ_ASSERT(imgCon); @@ -823,18 +836,18 @@ void nsImageBoxFrame::OnImageIsAnimated( void nsImageBoxFrame::OnFrameUpdate(imgIRequest* aRequest) { if ((0 == mRect.width) || (0 == mRect.height)) { return; } // Check if WebRender has interacted with this frame. If it has // we need to let it know that things have changed. const auto type = DisplayItemType::TYPE_XUL_IMAGE; - const auto providerId = aRequest->GetProviderId(); - if (WebRenderUserData::ProcessInvalidateForImage(this, type, providerId)) { + const auto producerId = aRequest->GetProducerId(); + if (WebRenderUserData::ProcessInvalidateForImage(this, type, producerId)) { return; } InvalidateLayer(type); } NS_IMPL_ISUPPORTS(nsImageBoxListener, imgINotificationObserver)