blob: 5f657c73420261be07b5275d8c7ac958230c0924 [file] [log] [blame]
robertphillips@google.come930a072014-04-03 00:34:27 +00001/*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrAtlas.h"
9#include "GrGpu.h"
10#include "GrLayerCache.h"
11
12/**
13 * PictureLayerKey just wraps a saveLayer's id in the picture for GrTHashTable.
14 */
15class GrLayerCache::PictureLayerKey {
16public:
skia.committer@gmail.coma9157722014-04-03 03:04:26 +000017 PictureLayerKey(uint32_t pictureID, int layerID)
robertphillips@google.come930a072014-04-03 00:34:27 +000018 : fPictureID(pictureID)
19 , fLayerID(layerID) {
20 }
21
22 uint32_t pictureID() const { return fPictureID; }
23 int layerID() const { return fLayerID; }
24
25 uint32_t getHash() const { return (fPictureID << 16) | fLayerID; }
26
commit-bot@chromium.org365cd312014-04-11 15:53:47 +000027 static bool LessThan(const GrCachedLayer& layer, const PictureLayerKey& key) {
robertphillips@google.come930a072014-04-03 00:34:27 +000028 if (layer.pictureID() == key.pictureID()) {
29 return layer.layerID() < key.layerID();
30 }
31
32 return layer.pictureID() < key.pictureID();
33 }
34
commit-bot@chromium.org365cd312014-04-11 15:53:47 +000035 static bool Equals(const GrCachedLayer& layer, const PictureLayerKey& key) {
robertphillips@google.come930a072014-04-03 00:34:27 +000036 return layer.pictureID() == key.pictureID() && layer.layerID() == key.layerID();
37 }
38
39private:
40 uint32_t fPictureID;
41 int fLayerID;
42};
43
robertphillips4ec84da2014-06-24 13:10:43 -070044GrLayerCache::GrLayerCache(GrContext* context)
robertphillips55e61f02014-06-29 15:08:31 -070045 : fContext(context) {
46 this->initAtlas();
robertphillips@google.come930a072014-04-03 00:34:27 +000047}
48
49GrLayerCache::~GrLayerCache() {
50}
51
robertphillips55e61f02014-06-29 15:08:31 -070052void GrLayerCache::initAtlas() {
robertphillips@google.come930a072014-04-03 00:34:27 +000053 static const int kAtlasTextureWidth = 1024;
54 static const int kAtlasTextureHeight = 1024;
55
robertphillips1d86ee82014-06-24 15:08:49 -070056 SkASSERT(NULL == fAtlas.get());
robertphillips@google.come930a072014-04-03 00:34:27 +000057
58 // The layer cache only gets 1 plot
59 SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
robertphillips1d86ee82014-06-24 15:08:49 -070060 fAtlas.reset(SkNEW_ARGS(GrAtlas, (fContext->getGpu(), kSkia8888_GrPixelConfig,
robertphillips55e61f02014-06-29 15:08:31 -070061 kRenderTarget_GrTextureFlagBit,
robertphillips1d86ee82014-06-24 15:08:49 -070062 textureSize, 1, 1, false)));
robertphillips@google.come930a072014-04-03 00:34:27 +000063}
64
65void GrLayerCache::freeAll() {
robertphillips55e61f02014-06-29 15:08:31 -070066 SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
67 for (int i = 0; i < fLayerHash.count(); ++i) {
68 this->unlock(layerArray[i]);
69 }
70
robertphillips@google.come930a072014-04-03 00:34:27 +000071 fLayerHash.deleteAll();
robertphillips55e61f02014-06-29 15:08:31 -070072
73 // The atlas only lets go of its texture when the atlas is deleted.
robertphillips1d86ee82014-06-24 15:08:49 -070074 fAtlas.free();
robertphillips55e61f02014-06-29 15:08:31 -070075 // GrLayerCache always assumes an atlas exists so recreate it. The atlas
76 // lazily allocates a replacement texture so reallocating a new
77 // atlas here won't disrupt a GrContext::contextDestroyed or freeGpuResources.
78 // TODO: Make GrLayerCache lazily allocate the atlas manager?
79 this->initAtlas();
robertphillips@google.come930a072014-04-03 00:34:27 +000080}
81
robertphillips9b14f262014-06-04 05:40:44 -070082GrCachedLayer* GrLayerCache::createLayer(const SkPicture* picture, int layerID) {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +000083 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
robertphillips55e61f02014-06-29 15:08:31 -070084
85 GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (picture->uniqueID(), layerID));
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +000086 fLayerHash.insert(PictureLayerKey(picture->uniqueID(), layerID), layer);
robertphillips@google.come930a072014-04-03 00:34:27 +000087 return layer;
88}
89
robertphillips4ec84da2014-06-24 13:10:43 -070090GrCachedLayer* GrLayerCache::findLayer(const SkPicture* picture, int layerID) {
91 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
92 return fLayerHash.find(PictureLayerKey(picture->uniqueID(), layerID));
93}
robertphillips@google.come930a072014-04-03 00:34:27 +000094
robertphillips9b14f262014-06-04 05:40:44 -070095GrCachedLayer* GrLayerCache::findLayerOrCreate(const SkPicture* picture, int layerID) {
commit-bot@chromium.org2b4e3702014-04-07 18:26:22 +000096 SkASSERT(picture->uniqueID() != SK_InvalidGenID);
commit-bot@chromium.org365cd312014-04-11 15:53:47 +000097 GrCachedLayer* layer = fLayerHash.find(PictureLayerKey(picture->uniqueID(), layerID));
robertphillips@google.come930a072014-04-03 00:34:27 +000098 if (NULL == layer) {
99 layer = this->createLayer(picture, layerID);
100 }
robertphillips4ec84da2014-06-24 13:10:43 -0700101
robertphillips@google.come930a072014-04-03 00:34:27 +0000102 return layer;
103}
robertphillips4ec84da2014-06-24 13:10:43 -0700104
105bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) {
robertphillips4ec84da2014-06-24 13:10:43 -0700106
robertphillips55e61f02014-06-29 15:08:31 -0700107 if (NULL != layer->texture()) {
108 // This layer is already locked
109#ifdef SK_DEBUG
110 if (!layer->rect().isEmpty()) {
111 // It claims to be atlased
112 SkASSERT(layer->rect().width() == desc.fWidth);
113 SkASSERT(layer->rect().height() == desc.fHeight);
114 }
115#endif
116 return true;
117 }
118
119#if USE_ATLAS
120 SkIPoint16 loc;
121 GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, desc.fWidth, desc.fHeight, NULL, &loc);
122 if (NULL != plot) {
123 GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY,
124 SkToS16(desc.fWidth), SkToS16(desc.fHeight));
125 layer->setTexture(fAtlas->getTexture(), bounds);
126 return false;
127 }
128#endif
129
130 // This path always uses a new scratch texture and (thus) doesn't cache anything.
robertphillips4ec84da2014-06-24 13:10:43 -0700131 // This can yield a lot of re-rendering
robertphillips55e61f02014-06-29 15:08:31 -0700132 layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kApprox_ScratchTexMatch),
133 GrIRect16::MakeEmpty());
robertphillips4ec84da2014-06-24 13:10:43 -0700134 return false;
135}
136
137void GrLayerCache::unlock(GrCachedLayer* layer) {
robertphillips55e61f02014-06-29 15:08:31 -0700138 if (NULL == layer || NULL == layer->texture()) {
robertphillips4ec84da2014-06-24 13:10:43 -0700139 return;
140 }
141
robertphillips55e61f02014-06-29 15:08:31 -0700142 // The atlas doesn't currently use a scratch texture (and we would have
143 // to free up space differently anyways)
144 // TODO: unlock atlas space when a recycling rectanizer is available
145 if (layer->texture() != fAtlas->getTexture()) {
146 fContext->unlockScratchTexture(layer->texture());
147 layer->setTexture(NULL, GrIRect16::MakeEmpty());
148 }
149}
150
151void GrLayerCache::purge(const SkPicture* picture) {
152 // This is somewhat of an abuse of GrTHashTable. We need to find all the
153 // layers associated with 'picture' but the usual hash calls only look for
154 // exact key matches. This code peeks into the hash table's innards to
155 // find all the 'picture'-related layers.
156 // TODO: use a different data structure for the layer hash?
157 SkTDArray<GrCachedLayer*> toBeRemoved;
158
159 const SkTDArray<GrCachedLayer*>& layerArray = fLayerHash.getArray();
160 for (int i = 0; i < fLayerHash.count(); ++i) {
161 if (picture->uniqueID() == layerArray[i]->pictureID()) {
162 *toBeRemoved.append() = layerArray[i];
163 }
164 }
165
166 for (int i = 0; i < toBeRemoved.count(); ++i) {
167 this->unlock(toBeRemoved[i]);
168
169 PictureLayerKey key(picture->uniqueID(), toBeRemoved[i]->layerID());
170 fLayerHash.remove(key, toBeRemoved[i]);
171 }
robertphillips4ec84da2014-06-24 13:10:43 -0700172}