aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrLayerHoister.cpp
diff options
context:
space:
mode:
authorGravatar robertphillips <robertphillips@google.com>2014-09-02 10:20:50 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-09-02 10:20:50 -0700
commit98d709bc8d0761df8e0ad35e92c06e500f0be50d (patch)
tree4a368c80280ce21d8015f52a37ca635600f83ec0 /src/gpu/GrLayerHoister.cpp
parent842b086a3c876061e1279d47e6009629c9818b03 (diff)
Reorganize Layer Hoisting code
With the new MultiPictureDraw API the GrContext will be performing the layer hoisting (instead of the SkGpuDevice). This CL being moving the layer hoisting functionality to GrLayerHoister rather then dumping it straight into GrContext. R=bsalomon@google.com Author: robertphillips@google.com Review URL: https://codereview.chromium.org/531733003
Diffstat (limited to 'src/gpu/GrLayerHoister.cpp')
-rw-r--r--src/gpu/GrLayerHoister.cpp210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/gpu/GrLayerHoister.cpp b/src/gpu/GrLayerHoister.cpp
new file mode 100644
index 0000000000..4c5b5a06e5
--- /dev/null
+++ b/src/gpu/GrLayerHoister.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "SkPictureRangePlayback.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+
+// Return true if any layers are suitable for hoisting
+bool GrLayerHoister::FindLayersToHoist(const GrAccelData *gpuData,
+ const SkPicture::OperationList* ops,
+ const SkRect& query,
+ bool pullForward[]) {
+ bool anyHoisted = false;
+ for (int i = 0; i < gpuData->numSaveLayers(); ++i) {
+ pullForward[i] = 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;
+
+ if (NULL != ops) {
+ // 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);
+
+ // For now we're saving all the layers in the GrAccelData 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 GrAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j);
+
+ 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;
+ anyHoisted = true;
+ }
+ }
+ } else {
+ // In this case there is no BBH associated with the picture. Pre-render
+ // all the layers that intersect the drawn region
+ for (int j = 0; j < gpuData->numSaveLayers(); ++j) {
+ const GrAccelData::SaveLayerInfo& info = gpuData->saveLayerInfo(j);
+
+ 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[j] = 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());
+
+ SkPictureRangePlayback rangePlayback(picture,
+ layer->start(),
+ layer->stop());
+ rangePlayback.draw(atlasCanvas, NULL);
+
+ 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());
+
+ SkPictureRangePlayback rangePlayback(picture,
+ layer->start(),
+ layer->stop());
+ rangePlayback.draw(layerCanvas, NULL);
+
+ 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(NULL != 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
+}
+