diff options
author | robertphillips <robertphillips@google.com> | 2014-10-28 07:21:44 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-28 07:21:44 -0700 |
commit | fd61ed0d7929cf85e0b936f48c72035af4c0a4b3 (patch) | |
tree | c91a06c5372a7846a01fe12a21e7597a790f88a3 | |
parent | ed53742e92c94d5ce20b982833725cf20546aaca (diff) |
Alter layer hoisting to only hoist layers for one canvas at a time
This CL alters layer hoisting to defer creation of the free floating layers until they are actually needed (rather than creating _all_ the hoisted layers at the start).
It also fixes a pre vs. post Concat bug with how matrices were being accumulated.
BUG=skia:2315
Review URL: https://codereview.chromium.org/657383004
-rw-r--r-- | src/core/SkMultiPictureDraw.cpp | 51 | ||||
-rw-r--r-- | src/gpu/GrLayerCache.cpp | 53 | ||||
-rw-r--r-- | src/gpu/GrLayerCache.h | 26 | ||||
-rw-r--r-- | src/gpu/GrLayerHoister.cpp | 185 | ||||
-rw-r--r-- | src/gpu/GrLayerHoister.h | 63 | ||||
-rw-r--r-- | src/gpu/GrPictureUtils.cpp | 2 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 27 | ||||
-rw-r--r-- | tests/GpuLayerCacheTest.cpp | 8 |
8 files changed, 255 insertions, 160 deletions
diff --git a/src/core/SkMultiPictureDraw.cpp b/src/core/SkMultiPictureDraw.cpp index 19b26ca607..5fe3c0ea52 100644 --- a/src/core/SkMultiPictureDraw.cpp +++ b/src/core/SkMultiPictureDraw.cpp @@ -63,7 +63,10 @@ void SkMultiPictureDraw::draw() { #ifndef SK_IGNORE_GPU_LAYER_HOISTING GrContext* context = NULL; - SkTDArray<GrHoistedLayer> atlased, nonAtlased, recycled; + // Start by collecting all the layers that are going to be atlased and render + // them (if necessary). Hoisting the free floating layers is deferred until + // drawing the canvas that requires them. + SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled; for (int i = 0; i < fDrawData.count(); ++i) { if (fDrawData[i].canvas->getGrContext() && @@ -80,28 +83,57 @@ void SkMultiPictureDraw::draw() { continue; } - GrLayerHoister::FindLayersToHoist(context, fDrawData[i].picture, - clipBounds, &atlased, &nonAtlased, &recycled); + // TODO: sorting the cacheable layers from smallest to largest + // would improve the packing and reduce the number of swaps + // TODO: another optimization would be to make a first pass to + // lock any required layer that is already in the atlas + GrLayerHoister::FindLayersToAtlas(context, fDrawData[i].picture, + clipBounds, + &atlasedNeedRendering, &atlasedRecycled); } } - GrReplacements replacements; - if (NULL != context) { - GrLayerHoister::DrawLayers(atlased, nonAtlased, recycled, &replacements); + GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering); } + + SkTDArray<GrHoistedLayer> needRendering, recycled; #endif for (int i = 0; i < fDrawData.count(); ++i) { #ifndef SK_IGNORE_GPU_LAYER_HOISTING if (fDrawData[i].canvas->getGrContext() && !fDrawData[i].paint && fDrawData[i].matrix.isIdentity()) { - // Render the entire picture using new layers + + SkRect clipBounds; + if (!fDrawData[i].canvas->getClipBounds(&clipBounds)) { + continue; + } + + // Find the layers required by this canvas. It will return atlased + // layers in the 'recycled' list since they have already been drawn. + GrLayerHoister::FindLayersToHoist(context, fDrawData[i].picture, + clipBounds, &needRendering, &recycled); + + GrLayerHoister::DrawLayers(context, needRendering); + + GrReplacements replacements; + + GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements); + GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements); + const SkMatrix initialMatrix = fDrawData[i].canvas->getTotalMatrix(); + // Render the entire picture using new layers GrRecordReplaceDraw(fDrawData[i].picture, fDrawData[i].canvas, &replacements, initialMatrix, NULL); - } else + + GrLayerHoister::UnlockLayers(context, needRendering); + GrLayerHoister::UnlockLayers(context, recycled); + + needRendering.rewind(); + recycled.rewind(); + } else #endif { fDrawData[i].canvas->drawPicture(fDrawData[i].picture, @@ -112,7 +144,8 @@ void SkMultiPictureDraw::draw() { #ifndef SK_IGNORE_GPU_LAYER_HOISTING if (NULL != context) { - GrLayerHoister::UnlockLayers(context, atlased, nonAtlased, recycled); + GrLayerHoister::UnlockLayers(context, atlasedNeedRendering); + GrLayerHoister::UnlockLayers(context, atlasedRecycled); } #endif diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp index 154c6f3621..ccb5bb0be4 100644 --- a/src/gpu/GrLayerCache.cpp +++ b/src/gpu/GrLayerCache.cpp @@ -161,29 +161,29 @@ GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID, return layer; } -bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool dontAtlas) { +bool GrLayerCache::tryToAtlas(GrCachedLayer* layer, + const GrTextureDesc& desc, + bool* needsRendering) { SkDEBUGCODE(GrAutoValidateLayer avl(fAtlas->getTexture(), layer);) + SkASSERT(PlausiblyAtlasable(desc.fWidth, desc.fHeight)); + if (layer->locked()) { // This layer is already locked -#ifdef SK_DEBUG - if (layer->isAtlased()) { - // It claims to be atlased - SkASSERT(!dontAtlas); - SkASSERT(layer->rect().width() == desc.fWidth); - SkASSERT(layer->rect().height() == desc.fHeight); - } -#endif - return false; + SkASSERT(layer->isAtlased()); + SkASSERT(layer->rect().width() == desc.fWidth); + SkASSERT(layer->rect().height() == desc.fHeight); + *needsRendering = false; + return true; } if (layer->isAtlased()) { // Hooray it is still in the atlas - make sure it stays there - SkASSERT(!dontAtlas); layer->setLocked(true); this->incPlotLock(layer->plot()->id()); - return false; - } else if (!dontAtlas && PlausiblyAtlasable(desc.fWidth, desc.fHeight)) { + *needsRendering = false; + return true; + } else { // Not in the atlas - will it fit? GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); if (NULL == pictInfo) { @@ -207,6 +207,7 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool do layer->setPlot(plot); layer->setLocked(true); this->incPlotLock(layer->plot()->id()); + *needsRendering = true; return true; } @@ -218,14 +219,26 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool do } } - // The texture wouldn't fit in the cache - give it it's own texture. - // This path always uses a new scratch texture and (thus) doesn't cache anything. - // This can yield a lot of re-rendering + return false; +} + +bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool* needsRendering) { + if (layer->locked()) { + // This layer is already locked + *needsRendering = false; + return true; + } + SkAutoTUnref<GrTexture> tex( fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch)); + if (!tex) { + return false; + } + layer->setTexture(tex, GrIRect16::MakeWH(SkToS16(desc.fWidth), SkToS16(desc.fHeight))); layer->setLocked(true); + *needsRendering = true; return true; } @@ -275,13 +288,7 @@ void GrLayerCache::validate() const { layer->validate(fAtlas->getTexture()); const GrPictureInfo* pictInfo = fPictureHash.find(layer->pictureID()); - if (pictInfo) { - // In aggressive cleanup mode a picture info should only exist if - // it has some atlased layers -#if !DISABLE_CACHING - SkASSERT(!pictInfo->fPlotUsage.isEmpty()); -#endif - } else { + if (!pictInfo) { // If there is no picture info for this picture then all of its // layers should be non-atlased. SkASSERT(!layer->isAtlased()); diff --git a/src/gpu/GrLayerCache.h b/src/gpu/GrLayerCache.h index adf904f833..58887d6405 100644 --- a/src/gpu/GrLayerCache.h +++ b/src/gpu/GrLayerCache.h @@ -208,10 +208,20 @@ public: const SkMatrix& ctm, const SkPaint* paint); - // Inform the cache that layer's cached image is now required. - // Return true if the layer must be re-rendered. Return false if the - // layer was found in the cache and can be reused. - bool lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool dontAtlas); + // Attempt to place 'layer' in the atlas. Return true on success; false on failure. + // When true is returned, 'needsRendering' will indicate if the layer must be (re)drawn. + // Additionally, the GPU resources will be locked. + bool tryToAtlas(GrCachedLayer* layer, const GrTextureDesc& desc, bool* needsRendering); + + // Attempt to lock the GPU resources required for a layer. Return true on success; + // false on failure. When true is returned 'needsRendering' will indicate if the + // layer must be (re)drawn. + // Note that atlased layers should already have been locked and rendered so only + // free floating layers will have 'needsRendering' set. + // Currently, this path always uses a new scratch texture for non-Atlased layers + // and (thus) doesn't cache anything. This can yield a lot of re-rendering. + // TODO: allow rediscovery of free-floating layers that are still in the resource cache. + bool lock(GrCachedLayer* layer, const GrTextureDesc& desc, bool* needsRendering); // addUse is just here to keep the API symmetric void addUse(GrCachedLayer* layer) { layer->addUse(); } @@ -235,6 +245,10 @@ public: void writeLayersToDisk(const SkString& dirName); #endif + static bool PlausiblyAtlasable(int width, int height) { + return width <= kPlotWidth && height <= kPlotHeight; + } + private: static const int kAtlasTextureWidth = 1024; static const int kAtlasTextureHeight = 1024; @@ -282,10 +296,6 @@ private: // Remove all the layers (and unlock any resources) associated with 'pictureID' void purge(uint32_t pictureID); - static bool PlausiblyAtlasable(int width, int height) { - return width <= kPlotWidth && height <= kPlotHeight; - } - void purgePlot(GrPlot* plot); // Try to find a purgeable plot and clear it out. Return true if a plot diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp index 3ae0e2d45f..272f0446b8 100644 --- a/src/gpu/GrLayerHoister.cpp +++ b/src/gpu/GrLayerHoister.cpp @@ -20,9 +20,9 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, const SkPicture* topLevelPicture, const GrAccelData::SaveLayerInfo& info, const SkIRect& layerRect, - SkTDArray<GrHoistedLayer>* atlased, - SkTDArray<GrHoistedLayer>* nonAtlased, - SkTDArray<GrHoistedLayer>* recycled) { + SkTDArray<GrHoistedLayer>* needRendering, + SkTDArray<GrHoistedLayer>* recycled, + bool attemptToAtlas) { const SkPicture* pict = info.fPicture ? info.fPicture : topLevelPicture; SkMatrix combined = SkMatrix::Concat(info.fPreMat, info.fLocalMat); @@ -40,24 +40,28 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, desc.fConfig = kSkia8888_GrPixelConfig; // TODO: need to deal with sample count - - bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested || - (layer->paint() && layer->paint()->getImageFilter()); - - bool needsRendering = layerCache->lock(layer, desc, disallowAtlasing); - if (NULL == layer->texture()) { + bool locked, needsRendering; + if (attemptToAtlas) { + locked = layerCache->tryToAtlas(layer, desc, &needsRendering); + } else { + locked = layerCache->lock(layer, desc, &needsRendering); + } + if (!locked) { // GPU resources could not be secured for the hoisting of this layer return; } + if (attemptToAtlas) { + SkASSERT(layer->isAtlased()); + } + GrHoistedLayer* hl; if (needsRendering) { - if (layer->isAtlased()) { - hl = atlased->append(); - } else { - hl = nonAtlased->append(); + if (!attemptToAtlas) { + SkASSERT(!layer->isAtlased()); } + hl = needRendering->append(); } else { hl = recycled->append(); } @@ -70,12 +74,13 @@ static void prepare_for_hoisting(GrLayerCache* layerCache, hl->fPreMat = info.fPreMat; } -// Return true if any layers are suitable for hoisting -bool GrLayerHoister::FindLayersToHoist(GrContext* context, +// Atlased layers must be small enough to fit in the atlas, not have a +// paint with an image filter and be neither nested nor nesting. +// TODO: allow leaf nested layers to appear in the atlas. +void GrLayerHoister::FindLayersToAtlas(GrContext* context, const SkPicture* topLevelPicture, const SkRect& query, SkTDArray<GrHoistedLayer>* atlased, - SkTDArray<GrHoistedLayer>* nonAtlased, SkTDArray<GrHoistedLayer>* recycled) { GrLayerCache* layerCache = context->getLayerCache(); @@ -85,31 +90,27 @@ bool GrLayerHoister::FindLayersToHoist(GrContext* context, const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); if (!topLevelData) { - return false; + return; } const GrAccelData *topLevelGPUData = static_cast<const GrAccelData*>(topLevelData); if (0 == topLevelGPUData->numSaveLayers()) { - return false; + return; } - bool anyHoisted = false; - - // The layer hoisting code will pre-render and cache an entire layer if most - // of it is being used (~70%) and it will fit in a texture. This is to allow - // such layers to be re-used for different clips/tiles. - // Small layers will additionally be atlased. - // The only limitation right now is that nested layers are currently not hoisted. - // Parent layers are hoisted but are never atlased (so that we never swap - // away from the atlas rendertarget when generating the hoisted layers). - atlased->setReserve(atlased->count() + topLevelGPUData->numSaveLayers()); - // Find and prepare for hoisting all the layers that intersect the query rect for (int i = 0; i < topLevelGPUData->numSaveLayers(); ++i) { - const GrAccelData::SaveLayerInfo& info = topLevelGPUData->saveLayerInfo(i); + // TODO: ignore perspective projected layers here? + bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested || + (info.fPaint && info.fPaint->getImageFilter()); + + if (disallowAtlasing) { + continue; + } + SkRect layerRect = SkRect::Make(info.fBounds); if (!layerRect.intersect(query)) { continue; @@ -118,18 +119,55 @@ bool GrLayerHoister::FindLayersToHoist(GrContext* context, SkIRect ir; layerRect.roundOut(&ir); - // TODO: ignore perspective projected layers here! - // TODO: once this code is more stable unsuitable layers can - // just be omitted during the optimization stage - if (info.fIsNested) { + if (!GrLayerCache::PlausiblyAtlasable(ir.width(), ir.height())) { continue; } - prepare_for_hoisting(layerCache, topLevelPicture, info, ir, atlased, nonAtlased, recycled); - anyHoisted = true; + prepare_for_hoisting(layerCache, topLevelPicture, info, ir, atlased, recycled, true); + } + +} + +void GrLayerHoister::FindLayersToHoist(GrContext* context, + const SkPicture* topLevelPicture, + const SkRect& query, + SkTDArray<GrHoistedLayer>* needRendering, + SkTDArray<GrHoistedLayer>* recycled) { + GrLayerCache* layerCache = context->getLayerCache(); + + layerCache->processDeletedPictures(); + + SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); + + const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key); + if (!topLevelData) { + return; + } + + const GrAccelData *topLevelGPUData = static_cast<const GrAccelData*>(topLevelData); + if (0 == topLevelGPUData->numSaveLayers()) { + return; } - return anyHoisted; + // Find and prepare for hoisting all the layers that intersect the query rect + for (int i = 0; i < topLevelGPUData->numSaveLayers(); ++i) { + const GrAccelData::SaveLayerInfo& info = topLevelGPUData->saveLayerInfo(i); + if (info.fIsNested) { + // Parent layers are currently hoisted while nested layers are not. + continue; + } + + SkRect layerRect = SkRect::Make(info.fBounds); + if (!layerRect.intersect(query)) { + continue; + } + + SkIRect ir; + layerRect.roundOut(&ir); + + prepare_for_hoisting(layerCache, topLevelPicture, info, ir, + needRendering, recycled, false); + } } static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) { @@ -138,8 +176,8 @@ static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* re result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); } -static void convert_layers_to_replacements(const SkTDArray<GrHoistedLayer>& layers, - GrReplacements* replacements) { +void GrLayerHoister::ConvertLayersToReplacements(const SkTDArray<GrHoistedLayer>& layers, + GrReplacements* replacements) { // TODO: just replace GrReplacements::ReplacementInfo with GrCachedLayer? for (int i = 0; i < layers.count(); ++i) { GrCachedLayer* layer = layers[i].fLayer; @@ -174,12 +212,8 @@ static void convert_layers_to_replacements(const SkTDArray<GrHoistedLayer>& laye } } -void GrLayerHoister::DrawLayers(GrContext* context, - const SkTDArray<GrHoistedLayer>& atlased, - const SkTDArray<GrHoistedLayer>& nonAtlased, - const SkTDArray<GrHoistedLayer>& recycled, - GrReplacements* replacements) { - // Render the atlased layers that require it +void GrLayerHoister::DrawLayersToAtlas(GrContext* context, + const SkTDArray<GrHoistedLayer>& atlased) { if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( @@ -210,38 +244,34 @@ void GrLayerHoister::DrawLayers(GrContext* context, atlasCanvas->clipRect(bound); // Since 'clear' doesn't respect the clip we need to draw a rect - // TODO: ensure none of the atlased layers contain a clear call! atlasCanvas->drawRect(bound, clearPaint); - // info.fCTM maps the layer's top/left to the origin. + // '-offset' maps the layer's top/left to the origin. // Since this layer is atlased, the top/left corner needs // to be offset to the correct location in the backing texture. SkMatrix initialCTM; - initialCTM.setTranslate(SkIntToScalar(-offset.fX), - SkIntToScalar(-offset.fY)); - initialCTM.postTranslate(bound.fLeft, bound.fTop); - initialCTM.postConcat(atlased[i].fPreMat); - - atlasCanvas->translate(SkIntToScalar(-offset.fX), - SkIntToScalar(-offset.fY)); - atlasCanvas->translate(bound.fLeft, bound.fTop); - atlasCanvas->concat(atlased[i].fPreMat); + initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); + initialCTM.preTranslate(bound.fLeft, bound.fTop); + initialCTM.preConcat(atlased[i].fPreMat); + + atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas, bound, - layer->start()+1, layer->stop(), initialCTM); + layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); } +} - // Render the non-atlased layers that require it - for (int i = 0; i < nonAtlased.count(); ++i) { - GrCachedLayer* layer = nonAtlased[i].fLayer; - const SkPicture* pict = nonAtlased[i].fPicture; - const SkIPoint& offset = nonAtlased[i].fOffset; +void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& layers) { + for (int i = 0; i < layers.count(); ++i) { + GrCachedLayer* layer = layers[i].fLayer; + const SkPicture* pict = layers[i].fPicture; + const SkIPoint& offset = layers[i].fOffset; // Each non-atlased layer has its own GrTexture SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( @@ -252,51 +282,36 @@ void GrLayerHoister::DrawLayers(GrContext* context, SkASSERT(0 == layer->rect().fLeft && 0 == layer->rect().fTop); // Add a rect clip to make sure the rendering doesn't - // extend beyond the boundaries of the atlased sub-rect + // extend beyond the boundaries of the layer SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), SkIntToScalar(layer->rect().fTop), SkIntToScalar(layer->rect().width()), SkIntToScalar(layer->rect().height())); - layerCanvas->clipRect(bound); // TODO: still useful? + layerCanvas->clipRect(bound); layerCanvas->clear(SK_ColorTRANSPARENT); SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); - initialCTM.postConcat(nonAtlased[i].fPreMat); + initialCTM.preConcat(layers[i].fPreMat); - layerCanvas->translate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); - layerCanvas->concat(nonAtlased[i].fPreMat); - layerCanvas->concat(nonAtlased[i].fLocalMat); + layerCanvas->setMatrix(initialCTM); + layerCanvas->concat(layers[i].fLocalMat); SkRecordPartialDraw(*pict->fRecord.get(), layerCanvas, bound, layer->start()+1, layer->stop(), initialCTM); layerCanvas->flush(); } - - convert_layers_to_replacements(atlased, replacements); - convert_layers_to_replacements(nonAtlased, replacements); - convert_layers_to_replacements(recycled, replacements); } void GrLayerHoister::UnlockLayers(GrContext* context, - const SkTDArray<GrHoistedLayer>& atlased, - const SkTDArray<GrHoistedLayer>& nonAtlased, - const SkTDArray<GrHoistedLayer>& recycled) { + const SkTDArray<GrHoistedLayer>& layers) { GrLayerCache* layerCache = context->getLayerCache(); - for (int i = 0; i < atlased.count(); ++i) { - layerCache->removeUse(atlased[i].fLayer); - } - - for (int i = 0; i < nonAtlased.count(); ++i) { - layerCache->removeUse(nonAtlased[i].fLayer); - } - - for (int i = 0; i < recycled.count(); ++i) { - layerCache->removeUse(recycled[i].fLayer); + for (int i = 0; i < layers.count(); ++i) { + layerCache->removeUse(layers[i].fLayer); } #if DISABLE_CACHING diff --git a/src/gpu/GrLayerHoister.h b/src/gpu/GrLayerHoister.h index 2b2eb0914b..82dd967bb3 100644 --- a/src/gpu/GrLayerHoister.h +++ b/src/gpu/GrLayerHoister.h @@ -33,47 +33,60 @@ public: class GrLayerHoister { public: + /** Find the layers in 'topLevelPicture' that can be atlased. Note that the discovered + layers can be inside nested sub-pictures. + @param context Owner of the layer cache (the source of new layers) + @param topLevelPicture The top-level picture that is about to be rendered + @param query The rectangle that is about to be drawn. + @param atlasedNeedRendering Out parameter storing the layers that + should be hoisted to the atlas + @param recycled Out parameter storing layers that are atlased but do not need rendering + */ + static void FindLayersToAtlas(GrContext* context, + const SkPicture* topLevelPicture, + const SkRect& query, + SkTDArray<GrHoistedLayer>* atlasedNeedRendering, + SkTDArray<GrHoistedLayer>* recycled); + /** Find the layers in 'topLevelPicture' that need hoisting. Note that the discovered layers can be inside nested sub-pictures. @param context Owner of the layer cache (the source of new layers) @param topLevelPicture The top-level picture that is about to be rendered @param query The rectangle that is about to be drawn. - @param atlased Out parameter storing the layers that should be hoisted to the atlas - @param nonAtlased Out parameter storing the layers that should be hoisted stand alone + @param needRendering Out parameter storing the layers that need rendering. + This should never include atlased layers. @param recycled Out parameter storing layers that need hoisting but not rendering - Return true if any layers are suitable for hoisting; false otherwise */ - static bool FindLayersToHoist(GrContext* context, + static void FindLayersToHoist(GrContext* context, const SkPicture* topLevelPicture, const SkRect& query, - SkTDArray<GrHoistedLayer>* atlased, - SkTDArray<GrHoistedLayer>* nonAtlased, + SkTDArray<GrHoistedLayer>* needRendering, SkTDArray<GrHoistedLayer>* recycled); - /** Draw the specified layers into either the atlas or free floating textures. + /** Draw the specified layers into the atlas. + @param context Owner of the layer cache (and thus the layers) + @param layers The layers to be drawn into the atlas + */ + static void DrawLayersToAtlas(GrContext* context, const SkTDArray<GrHoistedLayer>& layers); + + /** Draw the specified layers into their own individual textures. @param context Owner of the layer cache (and thus the layers) - @param atlased The layers to be drawn into the atlas - @param nonAtlased The layers to be drawn into their own textures - @param recycled Layers that don't need rendering but do need to go into the - replacements object - @param replacements The replacement structure to fill in with the rendered layer info + @param layers The layers to be drawn + */ + static void DrawLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& layers); + + /** Convert all the layers in 'layers' into replacement objects in 'replacements'. + @param layers The hoisted layers + @param replacements Replacement object that will be used for a replacement draw */ - static void DrawLayers(GrContext* context, - const SkTDArray<GrHoistedLayer>& atlased, - const SkTDArray<GrHoistedLayer>& nonAtlased, - const SkTDArray<GrHoistedLayer>& recycled, - GrReplacements* replacements); + static void ConvertLayersToReplacements(const SkTDArray<GrHoistedLayer>& layers, + GrReplacements* replacements); - /** Unlock unneeded layers in the layer cache. + /** Unlock a group of layers in the layer cache. @param context Owner of the layer cache (and thus the layers) - @param atlased Unneeded layers in the atlas - @param nonAtlased Unneeded layers in their own textures - @param recycled Unneeded layers that did not require rendering + @param layers Unneeded layers in the atlas */ - static void UnlockLayers(GrContext* context, - const SkTDArray<GrHoistedLayer>& atlased, - const SkTDArray<GrHoistedLayer>& nonAtlased, - const SkTDArray<GrHoistedLayer>& recycled); + static void UnlockLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& layers); }; #endif diff --git a/src/gpu/GrPictureUtils.cpp b/src/gpu/GrPictureUtils.cpp index e91980fa10..4cb6dda18b 100644 --- a/src/gpu/GrPictureUtils.cpp +++ b/src/gpu/GrPictureUtils.cpp @@ -126,7 +126,7 @@ private: dst.fBounds = newClip; dst.fLocalMat = src.fLocalMat; dst.fPreMat = src.fPreMat; - dst.fPreMat.preConcat(*fCTM); + dst.fPreMat.postConcat(*fCTM); if (src.fPaint) { dst.fPaint = SkNEW_ARGS(SkPaint, (*src.fPaint)); } diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 07b7f75e92..086dbdcdbd 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1816,23 +1816,36 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture return true; } - SkTDArray<GrHoistedLayer> atlased, nonAtlased, recycled; + SkTDArray<GrHoistedLayer> atlasedNeedRendering, atlasedRecycled; - if (!GrLayerHoister::FindLayersToHoist(fContext, mainPicture, clipBounds, - &atlased, &nonAtlased, &recycled)) { - return false; - } + GrLayerHoister::FindLayersToAtlas(fContext, mainPicture, + clipBounds, + &atlasedNeedRendering, &atlasedRecycled); + + GrLayerHoister::DrawLayersToAtlas(fContext, atlasedNeedRendering); + + SkTDArray<GrHoistedLayer> needRendering, recycled; + + GrLayerHoister::FindLayersToHoist(fContext, mainPicture, + clipBounds, + &needRendering, &recycled); + + GrLayerHoister::DrawLayers(fContext, needRendering); GrReplacements replacements; - GrLayerHoister::DrawLayers(fContext, atlased, nonAtlased, recycled, &replacements); + GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements); + GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements); // Render the entire picture using new layers const SkMatrix initialMatrix = mainCanvas->getTotalMatrix(); GrRecordReplaceDraw(mainPicture, mainCanvas, &replacements, initialMatrix, NULL); - GrLayerHoister::UnlockLayers(fContext, atlased, nonAtlased, recycled); + GrLayerHoister::UnlockLayers(fContext, needRendering); + GrLayerHoister::UnlockLayers(fContext, recycled); + GrLayerHoister::UnlockLayers(fContext, atlasedNeedRendering); + GrLayerHoister::UnlockLayers(fContext, atlasedRecycled); return true; } diff --git a/tests/GpuLayerCacheTest.cpp b/tests/GpuLayerCacheTest.cpp index 62f6ec7a8c..0a8d0d2442 100644 --- a/tests/GpuLayerCacheTest.cpp +++ b/tests/GpuLayerCacheTest.cpp @@ -66,10 +66,14 @@ static void lock_layer(skiatest::Reporter* reporter, desc.fHeight = 512; desc.fConfig = kSkia8888_GrPixelConfig; - bool needsRerendering = cache->lock(layer, desc, false); + bool needsRerendering; + bool inAtlas = cache->tryToAtlas(layer, desc, &needsRerendering); + if (!inAtlas) { + cache->lock(layer, desc, &needsRerendering); + } REPORTER_ASSERT(reporter, needsRerendering); - needsRerendering = cache->lock(layer, desc, false); + cache->lock(layer, desc, &needsRerendering); REPORTER_ASSERT(reporter, !needsRerendering); REPORTER_ASSERT(reporter, layer->texture()); |