OLD | NEW |
| (Empty) |
1 | |
2 /* | |
3 * Copyright 2010 Google Inc. | |
4 * | |
5 * Use of this source code is governed by a BSD-style license that can be | |
6 * found in the LICENSE file. | |
7 */ | |
8 | |
9 #include "GrAtlas.h" | |
10 #include "GrContext.h" | |
11 #include "GrGpu.h" | |
12 #include "GrRectanizer.h" | |
13 #include "GrTracing.h" | |
14 | |
15 /////////////////////////////////////////////////////////////////////////////// | |
16 | |
17 // for testing | |
18 #define FONT_CACHE_STATS 0 | |
19 #if FONT_CACHE_STATS | |
20 static int g_UploadCount = 0; | |
21 #endif | |
22 | |
23 GrPlot::GrPlot() | |
24 : fID(-1) | |
25 , fTexture(nullptr) | |
26 , fRects(nullptr) | |
27 , fAtlas(nullptr) | |
28 , fBytesPerPixel(1) | |
29 , fDirty(false) | |
30 , fBatchUploads(false) | |
31 { | |
32 fOffset.set(0, 0); | |
33 } | |
34 | |
35 GrPlot::~GrPlot() { | |
36 delete[] fPlotData; | |
37 fPlotData = nullptr; | |
38 delete fRects; | |
39 } | |
40 | |
41 void GrPlot::init(GrAtlas* atlas, int id, int offX, int offY, int width, int hei
ght, size_t bpp, | |
42 bool batchUploads) { | |
43 fID = id; | |
44 fRects = GrRectanizer::Factory(width, height); | |
45 fAtlas = atlas; | |
46 fOffset.set(offX * width, offY * height); | |
47 fBytesPerPixel = bpp; | |
48 fPlotData = nullptr; | |
49 fDirtyRect.setEmpty(); | |
50 fDirty = false; | |
51 fBatchUploads = batchUploads; | |
52 } | |
53 | |
54 static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset)
{ | |
55 loc->fX += offset.fX; | |
56 loc->fY += offset.fY; | |
57 } | |
58 | |
59 bool GrPlot::addSubImage(int width, int height, const void* image, SkIPoint16* l
oc) { | |
60 float percentFull = fRects->percentFull(); | |
61 if (!fRects->addRect(width, height, loc)) { | |
62 return false; | |
63 } | |
64 | |
65 // if batching uploads, create backing memory on first use | |
66 // once the plot is nearly full we will revert to uploading each subimage in
dividually | |
67 int plotWidth = fRects->width(); | |
68 int plotHeight = fRects->height(); | |
69 if (fBatchUploads && nullptr == fPlotData && 0.0f == percentFull) { | |
70 fPlotData = new unsigned char[fBytesPerPixel * plotWidth * plotHeight]; | |
71 memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight); | |
72 } | |
73 | |
74 // if we have backing memory, copy to the memory and set for future upload | |
75 if (fPlotData) { | |
76 const unsigned char* imagePtr = (const unsigned char*) image; | |
77 // point ourselves at the right starting spot | |
78 unsigned char* dataPtr = fPlotData; | |
79 dataPtr += fBytesPerPixel*plotWidth*loc->fY; | |
80 dataPtr += fBytesPerPixel*loc->fX; | |
81 // copy into the data buffer | |
82 for (int i = 0; i < height; ++i) { | |
83 memcpy(dataPtr, imagePtr, fBytesPerPixel*width); | |
84 dataPtr += fBytesPerPixel*plotWidth; | |
85 imagePtr += fBytesPerPixel*width; | |
86 } | |
87 | |
88 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height); | |
89 adjust_for_offset(loc, fOffset); | |
90 fDirty = true; | |
91 // otherwise, just upload the image directly | |
92 } else if (image) { | |
93 adjust_for_offset(loc, fOffset); | |
94 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTex
ture"); | |
95 fTexture->writePixels(loc->fX, loc->fY, width, height, fTexture->config(
), image, 0, | |
96 GrContext::kDontFlush_PixelOpsFlag); | |
97 } else { | |
98 adjust_for_offset(loc, fOffset); | |
99 } | |
100 | |
101 #if FONT_CACHE_STATS | |
102 ++g_UploadCount; | |
103 #endif | |
104 | |
105 return true; | |
106 } | |
107 | |
108 void GrPlot::resetRects() { | |
109 SkASSERT(fRects); | |
110 fRects->reset(); | |
111 } | |
112 | |
113 /////////////////////////////////////////////////////////////////////////////// | |
114 | |
115 GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrSurfaceFlags flags, | |
116 const SkISize& backingTextureSize, | |
117 int numPlotsX, int numPlotsY, bool batchUploads) { | |
118 fGpu = SkRef(gpu); | |
119 fPixelConfig = config; | |
120 fFlags = flags; | |
121 fBackingTextureSize = backingTextureSize; | |
122 fNumPlotsX = numPlotsX; | |
123 fNumPlotsY = numPlotsY; | |
124 fBatchUploads = batchUploads; | |
125 fTexture = nullptr; | |
126 | |
127 int textureWidth = fBackingTextureSize.width(); | |
128 int textureHeight = fBackingTextureSize.height(); | |
129 | |
130 int plotWidth = textureWidth / fNumPlotsX; | |
131 int plotHeight = textureHeight / fNumPlotsY; | |
132 | |
133 SkASSERT(plotWidth * fNumPlotsX == textureWidth); | |
134 SkASSERT(plotHeight * fNumPlotsY == textureHeight); | |
135 | |
136 // We currently do not support compressed atlases... | |
137 SkASSERT(!GrPixelConfigIsCompressed(config)); | |
138 | |
139 // set up allocated plots | |
140 size_t bpp = GrBytesPerPixel(fPixelConfig); | |
141 fPlotArray = new GrPlot[(fNumPlotsX * fNumPlotsY)]; | |
142 | |
143 GrPlot* currPlot = fPlotArray; | |
144 for (int y = numPlotsY-1; y >= 0; --y) { | |
145 for (int x = numPlotsX-1; x >= 0; --x) { | |
146 currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp
, batchUploads); | |
147 | |
148 // build LRU list | |
149 fPlotList.addToHead(currPlot); | |
150 ++currPlot; | |
151 } | |
152 } | |
153 } | |
154 | |
155 GrAtlas::~GrAtlas() { | |
156 SkSafeUnref(fTexture); | |
157 delete[] fPlotArray; | |
158 | |
159 fGpu->unref(); | |
160 #if FONT_CACHE_STATS | |
161 SkDebugf("Num uploads: %d\n", g_UploadCount); | |
162 #endif | |
163 } | |
164 | |
165 void GrAtlas::makeMRU(GrPlot* plot) { | |
166 if (fPlotList.head() == plot) { | |
167 return; | |
168 } | |
169 | |
170 fPlotList.remove(plot); | |
171 fPlotList.addToHead(plot); | |
172 }; | |
173 | |
174 GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage, | |
175 int width, int height, const void* image, | |
176 SkIPoint16* loc) { | |
177 // iterate through entire plot list for this atlas, see if we can find a hol
e | |
178 // last one was most recently added and probably most empty | |
179 for (int i = usage->fPlots.count()-1; i >= 0; --i) { | |
180 GrPlot* plot = usage->fPlots[i]; | |
181 // client may have plots from more than one atlas, must check for ours b
efore adding | |
182 if (this == plot->fAtlas && plot->addSubImage(width, height, image, loc)
) { | |
183 this->makeMRU(plot); | |
184 return plot; | |
185 } | |
186 } | |
187 | |
188 // before we get a new plot, make sure we have a backing texture | |
189 if (nullptr == fTexture) { | |
190 // TODO: Update this to use the cache rather than directly creating a te
xture. | |
191 GrSurfaceDesc desc; | |
192 desc.fFlags = fFlags; | |
193 desc.fWidth = fBackingTextureSize.width(); | |
194 desc.fHeight = fBackingTextureSize.height(); | |
195 desc.fConfig = fPixelConfig; | |
196 | |
197 fTexture = fGpu->createTexture(desc, true, nullptr, 0); | |
198 if (nullptr == fTexture) { | |
199 return nullptr; | |
200 } | |
201 } | |
202 | |
203 // now look through all allocated plots for one we can share, in MRU order | |
204 GrPlotList::Iter plotIter; | |
205 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart); | |
206 GrPlot* plot; | |
207 while ((plot = plotIter.get())) { | |
208 // make sure texture is set for quick lookup | |
209 plot->fTexture = fTexture; | |
210 if (plot->addSubImage(width, height, image, loc)) { | |
211 this->makeMRU(plot); | |
212 // new plot for atlas, put at end of array | |
213 SkASSERT(!usage->fPlots.contains(plot)); | |
214 *(usage->fPlots.append()) = plot; | |
215 return plot; | |
216 } | |
217 plotIter.next(); | |
218 } | |
219 | |
220 // If the above fails, then the current plot list has no room | |
221 return nullptr; | |
222 } | |
223 | |
224 void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) { | |
225 int index = usage->fPlots.find(const_cast<GrPlot*>(plot)); | |
226 if (index >= 0) { | |
227 usage->fPlots.remove(index); | |
228 } | |
229 } | |
OLD | NEW |