author | Michael Wu <mwu@mozilla.com> |
Wed, 29 Oct 2014 16:17:00 +0100 | |
changeset 213167 | b365ed0223370bc2562f1ef3fedd1936bcadcdad |
parent 213166 | cd5e08af2e43ee726a4081b878b4e280b7f6e564 |
child 213168 | 07daf18bf013c5649eda03804e137ccb9a958ba2 |
push id | 27742 |
push user | ryanvm@gmail.com |
push date | Thu, 30 Oct 2014 20:15:35 +0000 |
treeherder | mozilla-central@e0b505a37b1c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mattwoodrow |
bugs | 1081926 |
milestone | 36.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/canvas/CanvasImageCache.cpp +++ b/dom/canvas/CanvasImageCache.cpp @@ -77,16 +77,46 @@ public: { return HashGeneric(key->mImage, key->mCanvas); } enum { ALLOW_MEMMOVE = true }; nsAutoPtr<ImageCacheEntryData> mData; }; +class SimpleImageCacheEntry : public PLDHashEntryHdr { +public: + typedef imgIRequest& KeyType; + typedef const imgIRequest* KeyTypePointer; + + explicit SimpleImageCacheEntry(KeyTypePointer aKey) + : mRequest(const_cast<imgIRequest*>(aKey)) + {} + SimpleImageCacheEntry(const SimpleImageCacheEntry &toCopy) + : mRequest(toCopy.mRequest) + , mSourceSurface(toCopy.mSourceSurface) + {} + ~SimpleImageCacheEntry() {} + + bool KeyEquals(KeyTypePointer key) const + { + return key == mRequest; + } + + static KeyTypePointer KeyToPointer(KeyType key) { return &key; } + static PLDHashNumber HashKey(KeyTypePointer key) + { + return NS_PTR_TO_UINT32(key) >> 2; + } + enum { ALLOW_MEMMOVE = true }; + + nsCOMPtr<imgIRequest> mRequest; + RefPtr<SourceSurface> mSourceSurface; +}; + static bool sPrefsInitialized = false; static int32_t sCanvasImageCacheLimit = 0; class ImageCacheObserver; class ImageCache MOZ_FINAL : public nsExpirationTracker<ImageCacheEntryData,4> { public: // We use 3 generations of 1 second each to get a 2-3 seconds timeout. @@ -94,20 +124,22 @@ public: ImageCache(); ~ImageCache(); virtual void NotifyExpired(ImageCacheEntryData* aObject) { mTotal -= aObject->SizeInBytes(); RemoveObject(aObject); // Deleting the entry will delete aObject since the entry owns aObject + mSimpleCache.RemoveEntry(*aObject->mRequest); mCache.RemoveEntry(ImageCacheKey(aObject->mImage, aObject->mCanvas)); } nsTHashtable<ImageCacheEntry> mCache; + nsTHashtable<SimpleImageCacheEntry> mSimpleCache; size_t mTotal; nsRefPtr<ImageCacheObserver> mImageCacheObserver; }; static ImageCache* gImageCache = nullptr; // Listen memory-pressure event for image cache purge class ImageCacheObserver MOZ_FINAL : public nsIObserver @@ -225,16 +257,22 @@ CanvasImageCache::NotifyDrawImage(Elemen ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(entry->mData->mRequest)); } entry->mData->mILC = ilc; entry->mData->mSourceSurface = aSource; entry->mData->mSize = aSize; gImageCache->mTotal += entry->mData->SizeInBytes(); + + if (entry->mData->mRequest) { + SimpleImageCacheEntry* simpleentry = + gImageCache->mSimpleCache.PutEntry(*entry->mData->mRequest); + simpleentry->mSourceSurface = aSource; + } } if (!sCanvasImageCacheLimit) return; // Expire the image cache early if its larger than we want it to be. while (gImageCache->mTotal > size_t(sCanvasImageCacheLimit)) gImageCache->AgeOneGeneration(); @@ -258,16 +296,39 @@ CanvasImageCache::Lookup(Element* aImage return nullptr; gImageCache->MarkUsed(entry->mData); *aSize = entry->mData->mSize; return entry->mData->mSourceSurface; } +SourceSurface* +CanvasImageCache::SimpleLookup(Element* aImage) +{ + if (!gImageCache) + return nullptr; + + nsCOMPtr<imgIRequest> request; + nsCOMPtr<nsIImageLoadingContent> ilc = do_QueryInterface(aImage); + if (!ilc) + return nullptr; + + ilc->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(request)); + if (!request) + return nullptr; + + SimpleImageCacheEntry* entry = gImageCache->mSimpleCache.GetEntry(*request); + if (!entry) + return nullptr; + + return entry->mSourceSurface; +} + NS_IMPL_ISUPPORTS(CanvasImageCacheShutdownObserver, nsIObserver) NS_IMETHODIMP CanvasImageCacheShutdownObserver::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) { if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
--- a/dom/canvas/CanvasImageCache.h +++ b/dom/canvas/CanvasImageCache.h @@ -39,13 +39,20 @@ public: * Check whether aImage has recently been drawn into aCanvas. If we return * a non-null surface, then the image was recently drawn into the canvas * (with the same image request) and the returned surface contains the image * data, and the image size will be returned in aSize. */ static SourceSurface* Lookup(dom::Element* aImage, dom::HTMLCanvasElement* aCanvas, gfxIntSize* aSize); + + /** + * This is the same as Lookup, except it works on any image recently drawn + * into any canvas. Security checks need to be done again if using the + * results from this. + */ + static SourceSurface* SimpleLookup(dom::Element* aImage); }; } #endif /* CANVASIMAGECACHE_H_ */
--- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -3875,16 +3875,66 @@ ExtractSubrect(SourceSurface* aSurface, } *aSourceRect -= roundedOutSourceRect.TopLeft(); subrectDT->CopySurface(aSurface, roundedOutSourceRectInt, IntPoint()); return subrectDT->Snapshot(); } +// Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt +// to pull a SourceSurface from our cache. This allows us to avoid +// reoptimizing surfaces if content and canvas backends are different. +nsLayoutUtils::SurfaceFromElementResult +CanvasRenderingContext2D::CachedSurfaceFromElement(Element* aElement) +{ + nsLayoutUtils::SurfaceFromElementResult res; + + nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aElement); + if (!imageLoader) { + return res; + } + + nsCOMPtr<imgIRequest> imgRequest; + imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, + getter_AddRefs(imgRequest)); + if (!imgRequest) { + return res; + } + + uint32_t status; + if (NS_FAILED(imgRequest->GetImageStatus(&status)) || + !(status & imgIRequest::STATUS_LOAD_COMPLETE)) { + return res; + } + + nsCOMPtr<nsIPrincipal> principal; + if (NS_FAILED(imgRequest->GetImagePrincipal(getter_AddRefs(principal))) || + !principal) { + return res; + } + + res.mSourceSurface = CanvasImageCache::SimpleLookup(aElement); + if (!res.mSourceSurface) { + return res; + } + + int32_t corsmode = imgIRequest::CORS_NONE; + if (NS_SUCCEEDED(imgRequest->GetCORSMode(&corsmode))) { + res.mCORSUsed = corsmode != imgIRequest::CORS_NONE; + } + + res.mSize = ThebesIntSize(res.mSourceSurface->GetSize()); + res.mPrincipal = principal.forget(); + res.mIsWriteOnly = false; + res.mImageRequest = imgRequest.forget(); + + return res; +} + // // image // // drawImage(in HTMLImageElement image, in float dx, in float dy); // -- render image from 0,0 at dx,dy top-left coords // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh); // -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh @@ -3935,18 +3985,25 @@ CanvasRenderingContext2D::DrawImage(cons nsLayoutUtils::DirectDrawInfo drawInfo; if (!srcSurf) { // The canvas spec says that drawImage should draw the first frame // of animated images. We also don't want to rasterize vector images. uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME | nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS; + // The cache lookup can miss even if the image is already in the cache + // if the image is coming from a different element or cached for a + // different canvas. This covers the case when we miss due to caching + // for a different canvas, but CanvasImageCache should be fixed if we + // see misses due to different elements drawing the same image. nsLayoutUtils::SurfaceFromElementResult res = - nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget); + CachedSurfaceFromElement(element); + if (!res.mSourceSurface) + res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget); if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) { // Spec says to silently do nothing if the element is still loading. if (!res.mIsStillLoading) { error.Throw(NS_ERROR_NOT_AVAILABLE); } return; }
--- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -709,16 +709,19 @@ protected: mozilla::gfx::SurfaceFormat GetSurfaceFormat() const; /** * Update CurrentState().filter with the filter description for * CurrentState().filterChain. */ void UpdateFilter(); + nsLayoutUtils::SurfaceFromElementResult + CachedSurfaceFromElement(Element* aElement); + void DrawImage(const HTMLImageOrCanvasOrVideoElement &imgElt, double sx, double sy, double sw, double sh, double dx, double dy, double dw, double dh, uint8_t optional_argc, mozilla::ErrorResult& error); void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& image, mozilla::gfx::Rect* bounds, mozilla::gfx::Rect dest,