/* * Copyright 2014 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrLayerCache.h" #include "GrLayerHoister.h" #include "GrRecordReplaceDraw.h" #include "SkBigPicture.h" #include "SkCanvas.h" #include "SkGpuDevice.h" #include "SkLayerInfo.h" #include "SkRecordDraw.h" #include "SkSpecialImage.h" #include "SkSurface.h" #include "SkSurface_Gpu.h" // Create the layer information for the hoisted layer and secure the // required texture/render target resources. static void prepare_for_hoisting(GrLayerCache* layerCache, const SkPicture* topLevelPicture, const SkMatrix& initialMat, const SkLayerInfo::BlockInfo& info, const SkIRect& srcIR, const SkIRect& dstIR, SkTDArray* needRendering, SkTDArray* recycled, bool attemptToAtlas, int numSamples) { const SkPicture* pict = info.fPicture ? info.fPicture : topLevelPicture; GrCachedLayer* layer = layerCache->findLayerOrCreate(topLevelPicture->uniqueID(), SkToInt(info.fSaveLayerOpID), SkToInt(info.fRestoreOpID), srcIR, dstIR, initialMat, info.fKey, info.fKeySize, info.fPaint); GrSurfaceDesc desc; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fWidth = srcIR.width(); desc.fHeight = srcIR.height(); desc.fConfig = kSkia8888_GrPixelConfig; desc.fSampleCnt = numSamples; 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 (!attemptToAtlas) { SkASSERT(!layer->isAtlased()); } hl = needRendering->append(); } else { hl = recycled->append(); } layerCache->addUse(layer); hl->fLayer = layer; hl->fPicture = pict; hl->fLocalMat = info.fLocalMat; hl->fInitialMat = initialMat; hl->fPreMat = initialMat; hl->fPreMat.preConcat(info.fPreMat); } // Compute the source rect and return false if it is empty. static bool compute_source_rect(const SkLayerInfo::BlockInfo& info, const SkMatrix& initialMat, const SkIRect& dstIR, SkIRect* srcIR) { SkIRect clipBounds = dstIR; SkMatrix totMat = initialMat; totMat.preConcat(info.fPreMat); totMat.preConcat(info.fLocalMat); if (info.fPaint && info.fPaint->getImageFilter()) { clipBounds = info.fPaint->getImageFilter()->filterBounds(clipBounds, totMat); } if (!info.fSrcBounds.isEmpty()) { SkRect r; totMat.mapRect(&r, info.fSrcBounds); r.roundOut(srcIR); if (!srcIR->intersect(clipBounds)) { return false; } } else { *srcIR = clipBounds; } return true; } // 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 SkMatrix& initialMat, const SkRect& query, SkTDArray* atlased, SkTDArray* recycled, int numSamples) { if (0 != numSamples) { // MSAA layers are currently never atlased return; } GrLayerCache* layerCache = context->getLayerCache(); layerCache->processDeletedPictures(); const SkBigPicture::AccelData* topLevelData = nullptr; if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { topLevelData = bp->accelData(); } if (!topLevelData) { return; } const SkLayerInfo *topLevelGPUData = static_cast(topLevelData); if (0 == topLevelGPUData->numBlocks()) { return; } atlased->setReserve(atlased->count() + topLevelGPUData->numBlocks()); for (int i = 0; i < topLevelGPUData->numBlocks(); ++i) { const SkLayerInfo::BlockInfo& info = topLevelGPUData->block(i); // TODO: ignore perspective projected layers here? bool disallowAtlasing = info.fHasNestedLayers || info.fIsNested || (info.fPaint && info.fPaint->getImageFilter()); if (disallowAtlasing) { continue; } SkRect layerRect; initialMat.mapRect(&layerRect, info.fBounds); if (!layerRect.intersect(query)) { continue; } const SkIRect dstIR = layerRect.roundOut(); SkIRect srcIR; if (!compute_source_rect(info, initialMat, dstIR, &srcIR) || !GrLayerCache::PlausiblyAtlasable(srcIR.width(), srcIR.height())) { continue; } prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, srcIR, dstIR, atlased, recycled, true, 0); } } void GrLayerHoister::FindLayersToHoist(GrContext* context, const SkPicture* topLevelPicture, const SkMatrix& initialMat, const SkRect& query, SkTDArray* needRendering, SkTDArray* recycled, int numSamples) { GrLayerCache* layerCache = context->getLayerCache(); layerCache->processDeletedPictures(); const SkBigPicture::AccelData* topLevelData = nullptr; if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) { topLevelData = bp->accelData(); } if (!topLevelData) { return; } const SkLayerInfo *topLevelGPUData = static_cast(topLevelData); if (0 == topLevelGPUData->numBlocks()) { return; } // Find and prepare for hoisting all the layers that intersect the query rect for (int i = 0; i < topLevelGPUData->numBlocks(); ++i) { const SkLayerInfo::BlockInfo& info = topLevelGPUData->block(i); if (info.fIsNested) { // Parent layers are currently hoisted while nested layers are not. continue; } SkRect layerRect; initialMat.mapRect(&layerRect, info.fBounds); if (!layerRect.intersect(query)) { continue; } const SkIRect dstIR = layerRect.roundOut(); SkIRect srcIR; if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) { continue; } prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, srcIR, dstIR, needRendering, recycled, false, numSamples); } } void GrLayerHoister::DrawLayersToAtlas(GrContext* context, const SkTDArray& atlased) { if (atlased.count() > 0) { // All the atlased layers are rendered into the same GrTexture SkSurfaceProps props(0, kUnknown_SkPixelGeometry); auto surface(SkSurface::MakeRenderTargetDirect( atlased[0].fLayer->texture()->asRenderTarget(), &props)); SkCanvas* atlasCanvas = surface->getCanvas(); for (int i = 0; i < atlased.count(); ++i) { const GrCachedLayer* layer = atlased[i].fLayer; const SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture(); if (!pict) { // TODO: can we assume / assert this? continue; } const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();) SkASSERT(!layerPaint || !layerPaint->getImageFilter()); SkASSERT(!layer->filter()); atlasCanvas->save(); // Add a rect clip to make sure the rendering doesn't // extend beyond the boundaries of the atlased sub-rect const SkRect bound = SkRect::Make(layer->rect()); atlasCanvas->clipRect(bound); atlasCanvas->clear(0); // '-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.preTranslate(bound.fLeft, bound.fTop); initialCTM.preConcat(atlased[i].fPreMat); atlasCanvas->setMatrix(initialCTM); atlasCanvas->concat(atlased[i].fLocalMat); pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM); atlasCanvas->restore(); } atlasCanvas->flush(); } } void GrLayerHoister::FilterLayer(GrContext* context, SkGpuDevice* device, const GrHoistedLayer& info) { GrCachedLayer* layer = info.fLayer; SkASSERT(layer->filter()); static const int kDefaultCacheSize = 32 * 1024 * 1024; const SkIPoint filterOffset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); SkMatrix totMat(info.fPreMat); totMat.preConcat(info.fLocalMat); totMat.postTranslate(-SkIntToScalar(filterOffset.fX), -SkIntToScalar(filterOffset.fY)); SkASSERT(0 == layer->rect().fLeft && 0 == layer->rect().fTop); const SkIRect& clipBounds = layer->rect(); // This cache is transient, and is freed (along with all its contained // textures) when it goes out of scope. SkAutoTUnref cache(SkImageFilter::Cache::Create(kDefaultCacheSize)); SkImageFilter::Context filterContext(totMat, clipBounds, cache); SkImageFilter::DeviceProxy proxy(device); // TODO: should the layer hoister store stand alone layers as SkSpecialImages internally? const SkIRect subset = SkIRect::MakeWH(layer->texture()->width(), layer->texture()->height()); sk_sp img(SkSpecialImage::MakeFromGpu(&proxy, subset, kNeedNewImageUniqueID_SpecialImage, layer->texture())); SkIPoint offset = SkIPoint::Make(0, 0); sk_sp result(layer->filter()->filterImage(img.get(), filterContext, &offset)); if (!result) { // Filtering failed. Press on with the unfiltered version. return; } SkASSERT(result->peekTexture()); layer->setTexture(result->peekTexture(), result->subset(), false); layer->setOffset(offset); } void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray& layers) { for (int i = 0; i < layers.count(); ++i) { GrCachedLayer* layer = layers[i].fLayer; const SkBigPicture* pict = layers[i].fPicture->asSkBigPicture(); if (!pict) { // TODO: can we assume / assert this? continue; } const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop); // Each non-atlased layer has its own GrTexture SkSurfaceProps props(0, kUnknown_SkPixelGeometry); auto surface(SkSurface::MakeRenderTargetDirect( layer->texture()->asRenderTarget(), &props)); SkCanvas* layerCanvas = surface->getCanvas(); 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 layer const SkRect bound = SkRect::Make(layer->rect()); layerCanvas->clipRect(bound); layerCanvas->clear(SK_ColorTRANSPARENT); SkMatrix initialCTM; initialCTM.setTranslate(SkIntToScalar(-offset.fX), SkIntToScalar(-offset.fY)); initialCTM.preConcat(layers[i].fPreMat); layerCanvas->setMatrix(initialCTM); layerCanvas->concat(layers[i].fLocalMat); pict->partialPlayback(layerCanvas, layer->start()+1, layer->stop(), initialCTM); layerCanvas->flush(); if (layer->filter()) { SkSurface_Gpu* gpuSurf = static_cast(surface.get()); FilterLayer(context, gpuSurf->getDevice(), layers[i]); } } } void GrLayerHoister::UnlockLayers(GrContext* context, const SkTDArray& layers) { GrLayerCache* layerCache = context->getLayerCache(); for (int i = 0; i < layers.count(); ++i) { layerCache->removeUse(layers[i].fLayer); } SkDEBUGCODE(layerCache->validate();) } void GrLayerHoister::Begin(GrContext* context) { GrLayerCache* layerCache = context->getLayerCache(); layerCache->begin(); } void GrLayerHoister::End(GrContext* context) { GrLayerCache* layerCache = context->getLayerCache(); #if !GR_CACHE_HOISTED_LAYERS // This code completely clears out the atlas. It is required when // caching is disabled so the atlas doesn't fill up and force more // free floating layers layerCache->purgeAll(); #endif layerCache->end(); }