| /* |
| * 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 "SkCanvas.h" |
| #include "SkRecordDraw.h" |
| #include "SkSurface.h" |
| |
| // Return true if any layers are suitable for hoisting |
| bool GrLayerHoister::FindLayersToHoist(const GrAccelData *gpuData, |
| const SkRect& query, |
| bool pullForward[]) { |
| bool anyHoisted = false; |
| |
| // Layer hoisting pre-renders the entire layer since it will be cached and potentially |
| // reused with different clips (e.g., in different tiles). Because of this the |
| // clip will not be limiting the size of the pre-rendered layer. kSaveLayerMaxSize |
| // is used to limit which clips are pre-rendered. |
| static const int kSaveLayerMaxSize = 256; |
| |
| // Pre-render all the layers that intersect the query rect |
| for (int i = 0; i < gpuData->numSaveLayers(); ++i) { |
| pullForward[i] = false; |
| |
| const GrAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); |
| |
| SkRect layerRect = SkRect::MakeXYWH(SkIntToScalar(info.fOffset.fX), |
| SkIntToScalar(info.fOffset.fY), |
| SkIntToScalar(info.fSize.fWidth), |
| SkIntToScalar(info.fSize.fHeight)); |
| |
| if (!SkRect::Intersects(query, layerRect)) { |
| continue; |
| } |
| |
| // TODO: once this code is more stable unsuitable layers can |
| // just be omitted during the optimization stage |
| if (!info.fValid || |
| kSaveLayerMaxSize < info.fSize.fWidth || |
| kSaveLayerMaxSize < info.fSize.fHeight || |
| info.fIsNested) { |
| continue; |
| } |
| |
| pullForward[i] = true; |
| anyHoisted = true; |
| } |
| |
| return anyHoisted; |
| } |
| |
| void GrLayerHoister::DrawLayers(const SkPicture* picture, |
| const SkTDArray<GrCachedLayer*>& atlased, |
| const SkTDArray<GrCachedLayer*>& nonAtlased) { |
| // Render the atlased layers that require it |
| if (atlased.count() > 0) { |
| // All the atlased layers are rendered into the same GrTexture |
| SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( |
| atlased[0]->texture()->asRenderTarget(), |
| SkSurface::kStandard_TextRenderMode, |
| SkSurface::kDontClear_RenderTargetFlag)); |
| |
| SkCanvas* atlasCanvas = surface->getCanvas(); |
| |
| SkPaint paint; |
| paint.setColor(SK_ColorTRANSPARENT); |
| paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode))->unref(); |
| |
| for (int i = 0; i < atlased.count(); ++i) { |
| GrCachedLayer* layer = atlased[i]; |
| |
| atlasCanvas->save(); |
| |
| // Add a rect clip to make sure the rendering doesn't |
| // extend beyond the boundaries of the atlased sub-rect |
| SkRect bound = SkRect::MakeXYWH(SkIntToScalar(layer->rect().fLeft), |
| SkIntToScalar(layer->rect().fTop), |
| SkIntToScalar(layer->rect().width()), |
| SkIntToScalar(layer->rect().height())); |
| 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, paint); |
| |
| // info.fCTM 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. |
| atlasCanvas->translate(bound.fLeft, bound.fTop); |
| atlasCanvas->concat(layer->ctm()); |
| |
| SkRecordPartialDraw(*picture->fRecord.get(), atlasCanvas, bound, |
| layer->start()+1, layer->stop()); |
| |
| atlasCanvas->restore(); |
| } |
| |
| atlasCanvas->flush(); |
| } |
| |
| // Render the non-atlased layers that require it |
| for (int i = 0; i < nonAtlased.count(); ++i) { |
| GrCachedLayer* layer = nonAtlased[i]; |
| |
| // Each non-atlased layer has its own GrTexture |
| SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( |
| layer->texture()->asRenderTarget(), |
| SkSurface::kStandard_TextRenderMode, |
| SkSurface::kDontClear_RenderTargetFlag)); |
| |
| SkCanvas* layerCanvas = surface->getCanvas(); |
| |
| // Add a rect clip to make sure the rendering doesn't |
| // extend beyond the boundaries of the atlased sub-rect |
| 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->clear(SK_ColorTRANSPARENT); |
| |
| layerCanvas->concat(layer->ctm()); |
| |
| SkRecordPartialDraw(*picture->fRecord.get(), layerCanvas, bound, |
| layer->start()+1, layer->stop()); |
| |
| layerCanvas->flush(); |
| } |
| } |
| |
| void GrLayerHoister::UnlockLayers(GrLayerCache* layerCache, const SkPicture* picture) { |
| SkPicture::AccelData::Key key = GrAccelData::ComputeAccelDataKey(); |
| |
| const SkPicture::AccelData* data = picture->EXPERIMENTAL_getAccelData(key); |
| SkASSERT(data); |
| |
| const GrAccelData *gpuData = static_cast<const GrAccelData*>(data); |
| SkASSERT(0 != gpuData->numSaveLayers()); |
| |
| // unlock the layers |
| for (int i = 0; i < gpuData->numSaveLayers(); ++i) { |
| const GrAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); |
| |
| GrCachedLayer* layer = layerCache->findLayer(picture->uniqueID(), |
| info.fSaveLayerOpID, |
| info.fRestoreOpID, |
| info.fOriginXform); |
| layerCache->unlock(layer); |
| } |
| |
| #if DISABLE_CACHING |
| // 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->purge(picture->uniqueID()); |
| |
| layerCache->purgeAll(); |
| #endif |
| } |
| |