Index: src/gpu/SkGpuDevice.cpp |
=================================================================== |
--- src/gpu/SkGpuDevice.cpp (revision 14614) |
+++ src/gpu/SkGpuDevice.cpp (working copy) |
@@ -28,6 +28,7 @@ |
#include "SkMaskFilter.h" |
#include "SkPathEffect.h" |
#include "SkPicture.h" |
+#include "SkPicturePlayback.h" |
#include "SkRRect.h" |
#include "SkStroke.h" |
#include "SkSurface.h" |
@@ -1920,6 +1921,12 @@ |
GatherGPUInfo(picture, data); |
} |
+static void wrap_texture(GrTexture* texture, int width, int height, SkBitmap* result) { |
+ SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); |
+ result->setConfig(info); |
+ result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, texture)))->unref(); |
+} |
+ |
void SkGpuDevice::EXPERIMENTAL_purge(SkPicture* picture) { |
} |
@@ -1940,30 +1947,148 @@ |
pullForward[i] = false; |
} |
- SkIRect clip; |
+ SkRect clipBounds; |
+ if (!canvas->getClipBounds(&clipBounds)) { |
+ return true; |
+ } |
+ SkIRect query; |
+ clipBounds.roundOut(&query); |
- fClipData.getConservativeBounds(this->width(), this->height(), &clip, NULL); |
+ const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(query); |
- SkMatrix inv; |
- if (!fContext->getMatrix().invert(&inv)) { |
- return false; |
- } |
+ // This code 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; |
- SkRect r = SkRect::Make(clip); |
- inv.mapRect(&r); |
- r.roundOut(&clip); |
+ if (ops.valid()) { |
+ // In this case the picture has been generated with a BBH so we use |
+ // the BBH to limit the pre-rendering to just the layers needed to cover |
+ // the region being drawn |
+ for (int i = 0; i < ops.numOps(); ++i) { |
+ uint32_t offset = ops.offset(i); |
- const SkPicture::OperationList& ops = picture->EXPERIMENTAL_getActiveOps(clip); |
+ // For now we're saving all the layers in the GPUAccelData so they |
+ // can be nested. Additionally, the nested layers appear before |
+ // their parent in the list. |
+ for (int j = 0 ; j < gpuData->numSaveLayers(); ++j) { |
+ const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j); |
- for (int i = 0; i < ops.numOps(); ++i) { |
+ if (pullForward[j]) { |
+ continue; // already pulling forward |
+ } |
+ |
+ if (offset < info.fSaveLayerOpID || offset > info.fRestoreOpID) { |
+ continue; // the op isn't in this range |
+ } |
+ |
+ // 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; // this layer is unsuitable |
+ } |
+ |
+ pullForward[j] = true; |
+ } |
+ } |
+ } else { |
+ // In this case there is no BBH associated with the picture. Pre-render |
+ // all the layers |
+ // TODO: intersect the bounds of each layer with the clip region to |
+ // reduce the number of pre-rendered layers |
for (int j = 0; j < gpuData->numSaveLayers(); ++j) { |
const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j); |
- if (ops.offset(i) > info.fSaveLayerOpID && ops.offset(i) < info.fRestoreOpID) { |
- pullForward[j] = true; |
+ // 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[j] = true; |
} |
} |
- return false; |
+ SkPicturePlayback::PlaybackReplacements replacements; |
+ |
+ for (int i = 0; i < gpuData->numSaveLayers(); ++i) { |
+ if (pullForward[i]) { |
+ GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i); |
+ |
+ const GPUAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(i); |
+ |
+ if (NULL != picture->fPlayback) { |
+ SkPicturePlayback::PlaybackReplacements::ReplacementInfo* layerInfo = |
+ replacements.push(); |
+ layerInfo->fStart = info.fSaveLayerOpID; |
+ layerInfo->fStop = info.fRestoreOpID; |
+ layerInfo->fPos = info.fOffset; |
+ |
+ GrTextureDesc desc; |
+ desc.fFlags = kRenderTarget_GrTextureFlagBit; |
+ desc.fWidth = info.fSize.fWidth; |
+ desc.fHeight = info.fSize.fHeight; |
+ desc.fConfig = kSkia8888_GrPixelConfig; |
+ // TODO: need to deal with sample count |
+ |
+ bool bNeedsRendering = true; |
+ |
+ // This just uses scratch textures and doesn't cache the texture. |
+ // This can yield a lot of re-rendering |
+ if (NULL == layer->getTexture()) { |
+ layer->setTexture(fContext->lockAndRefScratchTexture(desc, |
+ GrContext::kApprox_ScratchTexMatch)); |
+ if (NULL == layer->getTexture()) { |
+ continue; |
+ } |
+ } else { |
+ bNeedsRendering = false; |
+ } |
+ |
+ layerInfo->fBM = SkNEW(SkBitmap); |
+ wrap_texture(layer->getTexture(), desc.fWidth, desc.fHeight, layerInfo->fBM); |
+ |
+ SkASSERT(info.fPaint); |
+ layerInfo->fPaint = info.fPaint; |
+ |
+ if (bNeedsRendering) { |
+ SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect( |
+ layer->getTexture()->asRenderTarget())); |
+ |
+ SkCanvas* canvas = surface->getCanvas(); |
+ |
+ canvas->setMatrix(info.fCTM); |
+ canvas->clear(SK_ColorTRANSPARENT); |
+ |
+ picture->fPlayback->setDrawLimits(info.fSaveLayerOpID, info.fRestoreOpID); |
+ picture->fPlayback->draw(*canvas, NULL); |
+ picture->fPlayback->setDrawLimits(0, 0); |
+ canvas->flush(); |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Playback using new layers |
+ picture->fPlayback->setReplacements(&replacements); |
+ picture->fPlayback->draw(*canvas, NULL); |
+ picture->fPlayback->setReplacements(NULL); |
+ |
+ for (int i = 0; i < gpuData->numSaveLayers(); ++i) { |
+ GrCachedLayer* layer = fContext->getLayerCache()->findLayerOrCreate(picture, i); |
+ |
+ if (NULL != layer->getTexture()) { |
+ fContext->unlockScratchTexture(layer->getTexture()); |
+ layer->setTexture(NULL); |
+ } |
+ } |
+ |
+ return true; |
} |