blob: c065dabd0b677deffb80ddfbf0ea4d7ae9859bc5 [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);
jvanverth8e80d172014-06-19 12:01:10 -070095 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
bsalomon81beccc2014-10-13 12:32:55 -070096 fTexture->writePixels(loc->fX, loc->fY, width, height, fTexture->config(), image, 0,
97 GrContext::kDontFlush_PixelOpsFlag);
robertphillips952841b2014-06-30 08:26:50 -070098 } else {
99 adjust_for_offset(loc, fOffset);
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000100 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000101
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000102#if FONT_CACHE_STATS
103 ++g_UploadCount;
104#endif
105
reed@google.comac10a2d2010-12-22 21:39:39 +0000106 return true;
107}
108
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000109void GrPlot::uploadToTexture() {
110 static const float kNearlyFullTolerance = 0.85f;
111
112 // should only do this if batching is enabled
113 SkASSERT(fBatchUploads);
114
115 if (fDirty) {
jvanverth8e80d172014-06-19 12:01:10 -0700116 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
bsalomon49f085d2014-09-05 13:34:00 -0700117 SkASSERT(fTexture);
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000118 // We pass the flag that does not force a flush. We assume our caller is
119 // smart and hasn't referenced the part of the texture we're about to update
120 // since the last flush.
jvanverth8e80d172014-06-19 12:01:10 -0700121 size_t rowBytes = fBytesPerPixel*fRects->width();
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000122 const unsigned char* dataPtr = fPlotData;
123 dataPtr += rowBytes*fDirtyRect.fTop;
124 dataPtr += fBytesPerPixel*fDirtyRect.fLeft;
bsalomon81beccc2014-10-13 12:32:55 -0700125 fTexture->writePixels(fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
126 fDirtyRect.width(), fDirtyRect.height(), fTexture->config(), dataPtr,
127 rowBytes, GrContext::kDontFlush_PixelOpsFlag);
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000128 fDirtyRect.setEmpty();
129 fDirty = false;
130 // If the Plot is nearly full, anything else we add will probably be small and one
131 // at a time, so free up the memory and after this upload any new images directly.
132 if (fRects->percentFull() > kNearlyFullTolerance) {
133 SkDELETE_ARRAY(fPlotData);
134 fPlotData = NULL;
135 }
136 }
137}
138
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000139void GrPlot::resetRects() {
bsalomon49f085d2014-09-05 13:34:00 -0700140 SkASSERT(fRects);
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000141 fRects->reset();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000142}
143
reed@google.comac10a2d2010-12-22 21:39:39 +0000144///////////////////////////////////////////////////////////////////////////////
145
bsalomonf2703d82014-10-28 14:33:06 -0700146GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrSurfaceFlags flags,
robertphillips1d86ee82014-06-24 15:08:49 -0700147 const SkISize& backingTextureSize,
148 int numPlotsX, int numPlotsY, bool batchUploads) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000149 fGpu = SkRef(gpu);
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000150 fPixelConfig = config;
robertphillips952841b2014-06-30 08:26:50 -0700151 fFlags = flags;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000152 fBackingTextureSize = backingTextureSize;
153 fNumPlotsX = numPlotsX;
154 fNumPlotsY = numPlotsY;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000155 fBatchUploads = batchUploads;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000156 fTexture = NULL;
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000157
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000158 int textureWidth = fBackingTextureSize.width();
159 int textureHeight = fBackingTextureSize.height();
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000160
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000161 int plotWidth = textureWidth / fNumPlotsX;
162 int plotHeight = textureHeight / fNumPlotsY;
163
164 SkASSERT(plotWidth * fNumPlotsX == textureWidth);
165 SkASSERT(plotHeight * fNumPlotsY == textureHeight);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000166
commit-bot@chromium.org6e7ddaa2014-05-30 13:55:58 +0000167 // We currently do not support compressed atlases...
168 SkASSERT(!GrPixelConfigIsCompressed(config));
169
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000170 // set up allocated plots
robertphillips@google.com8b169312013-10-15 17:47:36 +0000171 size_t bpp = GrBytesPerPixel(fPixelConfig);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000172 fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000173
174 GrPlot* currPlot = fPlotArray;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000175 for (int y = numPlotsY-1; y >= 0; --y) {
176 for (int x = numPlotsX-1; x >= 0; --x) {
robertphillips17dabfc2014-07-16 13:26:24 -0700177 currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp, batchUploads);
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000178
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000179 // build LRU list
180 fPlotList.addToHead(currPlot);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000181 ++currPlot;
182 }
183 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000184}
185
robertphillips1d86ee82014-06-24 15:08:49 -0700186GrAtlas::~GrAtlas() {
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000187 SkSafeUnref(fTexture);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000188 SkDELETE_ARRAY(fPlotArray);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000189
reed@google.comac10a2d2010-12-22 21:39:39 +0000190 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000191#if FONT_CACHE_STATS
tfarina38406c82014-10-31 07:11:12 -0700192 SkDebugf("Num uploads: %d\n", g_UploadCount);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000193#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000194}
195
robertphillips1d86ee82014-06-24 15:08:49 -0700196void GrAtlas::makeMRU(GrPlot* plot) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000197 if (fPlotList.head() == plot) {
198 return;
199 }
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000200
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000201 fPlotList.remove(plot);
202 fPlotList.addToHead(plot);
203};
204
robertphillips1d86ee82014-06-24 15:08:49 -0700205GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage,
206 int width, int height, const void* image,
207 SkIPoint16* loc) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000208 // iterate through entire plot list for this atlas, see if we can find a hole
209 // last one was most recently added and probably most empty
robertphillips1d86ee82014-06-24 15:08:49 -0700210 for (int i = usage->fPlots.count()-1; i >= 0; --i) {
211 GrPlot* plot = usage->fPlots[i];
jvanverth294c3262014-10-10 11:36:12 -0700212 // client may have plots from more than one atlas, must check for ours before adding
213 if (this == plot->fAtlas && plot->addSubImage(width, height, image, loc)) {
robertphillips1d86ee82014-06-24 15:08:49 -0700214 this->makeMRU(plot);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000215 return plot;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000216 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000217 }
218
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000219 // before we get a new plot, make sure we have a backing texture
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000220 if (NULL == fTexture) {
bsalomon@google.com95ed55a2013-01-24 14:46:47 +0000221 // TODO: Update this to use the cache rather than directly creating a texture.
bsalomonf2703d82014-10-28 14:33:06 -0700222 GrSurfaceDesc desc;
223 desc.fFlags = fFlags;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000224 desc.fWidth = fBackingTextureSize.width();
225 desc.fHeight = fBackingTextureSize.height();
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000226 desc.fConfig = fPixelConfig;
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000227
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000228 fTexture = fGpu->createTexture(desc, NULL, 0);
229 if (NULL == fTexture) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000230 return NULL;
231 }
232 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000233
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000234 // now look through all allocated plots for one we can share, in MRU order
235 GrPlotList::Iter plotIter;
236 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
237 GrPlot* plot;
bsalomon49f085d2014-09-05 13:34:00 -0700238 while ((plot = plotIter.get())) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000239 // make sure texture is set for quick lookup
240 plot->fTexture = fTexture;
241 if (plot->addSubImage(width, height, image, loc)) {
robertphillips1d86ee82014-06-24 15:08:49 -0700242 this->makeMRU(plot);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000243 // new plot for atlas, put at end of array
robertphillips1d86ee82014-06-24 15:08:49 -0700244 SkASSERT(!usage->fPlots.contains(plot));
245 *(usage->fPlots.append()) = plot;
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000246 return plot;
247 }
248 plotIter.next();
reed@google.comac10a2d2010-12-22 21:39:39 +0000249 }
250
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000251 // If the above fails, then the current plot list has no room
252 return NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000253}
254
robertphillipsc4f30b12014-07-13 10:09:42 -0700255void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) {
robertphillips1d86ee82014-06-24 15:08:49 -0700256 int index = usage->fPlots.find(const_cast<GrPlot*>(plot));
257 if (index >= 0) {
258 usage->fPlots.remove(index);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000259 }
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000260}
261
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000262// get a plot that's not being used by the current draw
robertphillips1d86ee82014-06-24 15:08:49 -0700263GrPlot* GrAtlas::getUnusedPlot() {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000264 GrPlotList::Iter plotIter;
265 plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
266 GrPlot* plot;
bsalomon49f085d2014-09-05 13:34:00 -0700267 while ((plot = plotIter.get())) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000268 if (plot->drawToken().isIssued()) {
269 return plot;
270 }
271 plotIter.prev();
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000272 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000273
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000274 return NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000275}
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000276
robertphillips1d86ee82014-06-24 15:08:49 -0700277void GrAtlas::uploadPlotsToTexture() {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000278 if (fBatchUploads) {
279 GrPlotList::Iter plotIter;
280 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
281 GrPlot* plot;
bsalomon49f085d2014-09-05 13:34:00 -0700282 while ((plot = plotIter.get())) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000283 plot->uploadToTexture();
284 plotIter.next();
285 }
286 }
287}