blob: e75f859fec5e3bea4e3498101593cd32a7004a88 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@google.comac10a2d2010-12-22 21:39:39 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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.
reed@google.comac10a2d2010-12-22 21:39:39 +00007 */
8
reed@google.comac10a2d2010-12-22 21:39:39 +00009#include "GrAtlas.h"
bsalomon@google.com6f379512011-11-16 20:36:03 +000010#include "GrContext.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000011#include "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000012#include "GrRectanizer.h"
jvanverth8e80d172014-06-19 12:01:10 -070013#include "GrTracing.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000014
reed@google.comac10a2d2010-12-22 21:39:39 +000015///////////////////////////////////////////////////////////////////////////////
16
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000017// for testing
18#define FONT_CACHE_STATS 0
19#if FONT_CACHE_STATS
20static int g_UploadCount = 0;
21#endif
22
robertphillips17dabfc2014-07-16 13:26:24 -070023GrPlot::GrPlot()
24 : fDrawToken(NULL, 0)
25 , fID(-1)
26 , fTexture(NULL)
27 , fRects(NULL)
28 , fAtlas(NULL)
29 , fBytesPerPixel(1)
30 , fDirty(false)
31 , fBatchUploads(false)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000032{
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000033 fOffset.set(0, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +000034}
35
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000036GrPlot::~GrPlot() {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000037 SkDELETE_ARRAY(fPlotData);
38 fPlotData = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000039 delete fRects;
reed@google.comac10a2d2010-12-22 21:39:39 +000040}
41
robertphillips17dabfc2014-07-16 13:26:24 -070042void GrPlot::init(GrAtlas* atlas, int id, int offX, int offY, int width, int height, size_t bpp,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000043 bool batchUploads) {
robertphillips17dabfc2014-07-16 13:26:24 -070044 fID = id;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000045 fRects = GrRectanizer::Factory(width, height);
robertphillips1d86ee82014-06-24 15:08:49 -070046 fAtlas = atlas;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000047 fOffset.set(offX * width, offY * height);
48 fBytesPerPixel = bpp;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000049 fPlotData = NULL;
50 fDirtyRect.setEmpty();
51 fDirty = false;
52 fBatchUploads = batchUploads;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000053}
54
robertphillipsd5373412014-06-02 10:20:14 -070055static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000056 loc->fX += offset.fX;
57 loc->fY += offset.fY;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000058}
59
robertphillips952841b2014-06-30 08:26:50 -070060bool GrPlot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000061 float percentFull = fRects->percentFull();
commit-bot@chromium.orgf9529242014-02-14 18:41:47 +000062 if (!fRects->addRect(width, height, loc)) {
reed@google.comac10a2d2010-12-22 21:39:39 +000063 return false;
64 }
65
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000066 // if batching uploads, create backing memory on first use
67 // once the plot is nearly full we will revert to uploading each subimage individually
68 int plotWidth = fRects->width();
69 int plotHeight = fRects->height();
70 if (fBatchUploads && NULL == fPlotData && 0.0f == percentFull) {
71 fPlotData = SkNEW_ARRAY(unsigned char, fBytesPerPixel*plotWidth*plotHeight);
72 memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight);
73 }
74
75 // if we have backing memory, copy to the memory and set for future upload
bsalomon49f085d2014-09-05 13:34:00 -070076 if (fPlotData) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000077 const unsigned char* imagePtr = (const unsigned char*) image;
78 // point ourselves at the right starting spot
79 unsigned char* dataPtr = fPlotData;
80 dataPtr += fBytesPerPixel*plotWidth*loc->fY;
81 dataPtr += fBytesPerPixel*loc->fX;
82 // copy into the data buffer
83 for (int i = 0; i < height; ++i) {
84 memcpy(dataPtr, imagePtr, fBytesPerPixel*width);
85 dataPtr += fBytesPerPixel*plotWidth;
86 imagePtr += fBytesPerPixel*width;
87 }
88
89 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
90 adjust_for_offset(loc, fOffset);
91 fDirty = true;
92 // otherwise, just upload the image directly
bsalomon49f085d2014-09-05 13:34:00 -070093 } else if (image) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000094 adjust_for_offset(loc, fOffset);
95 GrContext* context = fTexture->getContext();
jvanverth8e80d172014-06-19 12:01:10 -070096 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000097 context->writeTexturePixels(fTexture,
98 loc->fX, loc->fY, width, height,
99 fTexture->config(), image, 0,
100 GrContext::kDontFlush_PixelOpsFlag);
robertphillips952841b2014-06-30 08:26:50 -0700101 } else {
102 adjust_for_offset(loc, fOffset);
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000103 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000104
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000105#if FONT_CACHE_STATS
106 ++g_UploadCount;
107#endif
108
reed@google.comac10a2d2010-12-22 21:39:39 +0000109 return true;
110}
111
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000112void GrPlot::uploadToTexture() {
113 static const float kNearlyFullTolerance = 0.85f;
114
115 // should only do this if batching is enabled
116 SkASSERT(fBatchUploads);
117
118 if (fDirty) {
jvanverth8e80d172014-06-19 12:01:10 -0700119 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
bsalomon49f085d2014-09-05 13:34:00 -0700120 SkASSERT(fTexture);
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000121 GrContext* context = fTexture->getContext();
122 // We pass the flag that does not force a flush. We assume our caller is
123 // smart and hasn't referenced the part of the texture we're about to update
124 // since the last flush.
jvanverth8e80d172014-06-19 12:01:10 -0700125 size_t rowBytes = fBytesPerPixel*fRects->width();
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000126 const unsigned char* dataPtr = fPlotData;
127 dataPtr += rowBytes*fDirtyRect.fTop;
128 dataPtr += fBytesPerPixel*fDirtyRect.fLeft;
129 context->writeTexturePixels(fTexture,
skia.committer@gmail.coma1633da2014-05-15 03:03:58 +0000130 fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000131 fDirtyRect.width(), fDirtyRect.height(),
skia.committer@gmail.coma1633da2014-05-15 03:03:58 +0000132 fTexture->config(), dataPtr,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000133 rowBytes,
134 GrContext::kDontFlush_PixelOpsFlag);
135 fDirtyRect.setEmpty();
136 fDirty = false;
137 // If the Plot is nearly full, anything else we add will probably be small and one
138 // at a time, so free up the memory and after this upload any new images directly.
139 if (fRects->percentFull() > kNearlyFullTolerance) {
140 SkDELETE_ARRAY(fPlotData);
141 fPlotData = NULL;
142 }
143 }
144}
145
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000146void GrPlot::resetRects() {
bsalomon49f085d2014-09-05 13:34:00 -0700147 SkASSERT(fRects);
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000148 fRects->reset();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000149}
150
reed@google.comac10a2d2010-12-22 21:39:39 +0000151///////////////////////////////////////////////////////////////////////////////
152
robertphillips952841b2014-06-30 08:26:50 -0700153GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrTextureFlags flags,
robertphillips1d86ee82014-06-24 15:08:49 -0700154 const SkISize& backingTextureSize,
155 int numPlotsX, int numPlotsY, bool batchUploads) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000156 fGpu = SkRef(gpu);
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000157 fPixelConfig = config;
robertphillips952841b2014-06-30 08:26:50 -0700158 fFlags = flags;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000159 fBackingTextureSize = backingTextureSize;
160 fNumPlotsX = numPlotsX;
161 fNumPlotsY = numPlotsY;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000162 fBatchUploads = batchUploads;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000163 fTexture = NULL;
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000164
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000165 int textureWidth = fBackingTextureSize.width();
166 int textureHeight = fBackingTextureSize.height();
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000167
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000168 int plotWidth = textureWidth / fNumPlotsX;
169 int plotHeight = textureHeight / fNumPlotsY;
170
171 SkASSERT(plotWidth * fNumPlotsX == textureWidth);
172 SkASSERT(plotHeight * fNumPlotsY == textureHeight);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000173
commit-bot@chromium.org6e7ddaa2014-05-30 13:55:58 +0000174 // We currently do not support compressed atlases...
175 SkASSERT(!GrPixelConfigIsCompressed(config));
176
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000177 // set up allocated plots
robertphillips@google.com8b169312013-10-15 17:47:36 +0000178 size_t bpp = GrBytesPerPixel(fPixelConfig);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000179 fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000180
181 GrPlot* currPlot = fPlotArray;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000182 for (int y = numPlotsY-1; y >= 0; --y) {
183 for (int x = numPlotsX-1; x >= 0; --x) {
robertphillips17dabfc2014-07-16 13:26:24 -0700184 currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp, batchUploads);
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000185
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000186 // build LRU list
187 fPlotList.addToHead(currPlot);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000188 ++currPlot;
189 }
190 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000191}
192
robertphillips1d86ee82014-06-24 15:08:49 -0700193GrAtlas::~GrAtlas() {
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000194 SkSafeUnref(fTexture);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000195 SkDELETE_ARRAY(fPlotArray);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000196
reed@google.comac10a2d2010-12-22 21:39:39 +0000197 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000198#if FONT_CACHE_STATS
199 GrPrintf("Num uploads: %d\n", g_UploadCount);
200#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000201}
202
robertphillips1d86ee82014-06-24 15:08:49 -0700203void GrAtlas::makeMRU(GrPlot* plot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000204 if (fPlotList.head() == plot) {
205 return;
206 }
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000207
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000208 fPlotList.remove(plot);
209 fPlotList.addToHead(plot);
210};
211
robertphillips1d86ee82014-06-24 15:08:49 -0700212GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage,
213 int width, int height, const void* image,
214 SkIPoint16* loc) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000215 // iterate through entire plot list for this atlas, see if we can find a hole
216 // last one was most recently added and probably most empty
robertphillips1d86ee82014-06-24 15:08:49 -0700217 for (int i = usage->fPlots.count()-1; i >= 0; --i) {
218 GrPlot* plot = usage->fPlots[i];
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000219 if (plot->addSubImage(width, height, image, loc)) {
robertphillips1d86ee82014-06-24 15:08:49 -0700220 this->makeMRU(plot);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000221 return plot;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000222 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000223 }
224
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000225 // before we get a new plot, make sure we have a backing texture
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000226 if (NULL == fTexture) {
bsalomon@google.com95ed55a2013-01-24 14:46:47 +0000227 // TODO: Update this to use the cache rather than directly creating a texture.
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000228 GrTextureDesc desc;
robertphillips952841b2014-06-30 08:26:50 -0700229 desc.fFlags = fFlags | kDynamicUpdate_GrTextureFlagBit;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000230 desc.fWidth = fBackingTextureSize.width();
231 desc.fHeight = fBackingTextureSize.height();
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000232 desc.fConfig = fPixelConfig;
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000233
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000234 fTexture = fGpu->createTexture(desc, NULL, 0);
235 if (NULL == fTexture) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000236 return NULL;
237 }
238 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000239
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000240 // now look through all allocated plots for one we can share, in MRU order
241 GrPlotList::Iter plotIter;
242 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
243 GrPlot* plot;
bsalomon49f085d2014-09-05 13:34:00 -0700244 while ((plot = plotIter.get())) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000245 // make sure texture is set for quick lookup
246 plot->fTexture = fTexture;
247 if (plot->addSubImage(width, height, image, loc)) {
robertphillips1d86ee82014-06-24 15:08:49 -0700248 this->makeMRU(plot);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000249 // new plot for atlas, put at end of array
robertphillips1d86ee82014-06-24 15:08:49 -0700250 SkASSERT(!usage->fPlots.contains(plot));
251 *(usage->fPlots.append()) = plot;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000252 return plot;
253 }
254 plotIter.next();
reed@google.comac10a2d2010-12-22 21:39:39 +0000255 }
256
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000257 // If the above fails, then the current plot list has no room
258 return NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000259}
260
robertphillipsc4f30b12014-07-13 10:09:42 -0700261void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) {
robertphillips1d86ee82014-06-24 15:08:49 -0700262 int index = usage->fPlots.find(const_cast<GrPlot*>(plot));
263 if (index >= 0) {
264 usage->fPlots.remove(index);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000265 }
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000266}
267
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000268// get a plot that's not being used by the current draw
robertphillips1d86ee82014-06-24 15:08:49 -0700269GrPlot* GrAtlas::getUnusedPlot() {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000270 GrPlotList::Iter plotIter;
271 plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
272 GrPlot* plot;
bsalomon49f085d2014-09-05 13:34:00 -0700273 while ((plot = plotIter.get())) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000274 if (plot->drawToken().isIssued()) {
275 return plot;
276 }
277 plotIter.prev();
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000278 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000279
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000280 return NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000281}
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000282
robertphillips1d86ee82014-06-24 15:08:49 -0700283void GrAtlas::uploadPlotsToTexture() {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000284 if (fBatchUploads) {
285 GrPlotList::Iter plotIter;
286 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
287 GrPlot* plot;
bsalomon49f085d2014-09-05 13:34:00 -0700288 while ((plot = plotIter.get())) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000289 plot->uploadToTexture();
290 plotIter.next();
291 }
292 }
293}