blob: fc5b7c3d628689a9e611b655de0cf8331721c326 [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"
reed@google.comac10a2d2010-12-22 21:39:39 +000013
reed@google.comac10a2d2010-12-22 21:39:39 +000014///////////////////////////////////////////////////////////////////////////////
15
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000016// for testing
17#define FONT_CACHE_STATS 0
18#if FONT_CACHE_STATS
19static int g_UploadCount = 0;
20#endif
21
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000022GrPlot::GrPlot() : fDrawToken(NULL, 0)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000023 , fTexture(NULL)
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000024 , fRects(NULL)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000025 , fAtlasMgr(NULL)
26 , fBytesPerPixel(1)
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000027 , fDirty(false)
28 , fBatchUploads(false)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000029{
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000030 fOffset.set(0, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +000031}
32
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000033GrPlot::~GrPlot() {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000034 SkDELETE_ARRAY(fPlotData);
35 fPlotData = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000036 delete fRects;
reed@google.comac10a2d2010-12-22 21:39:39 +000037}
38
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000039void GrPlot::init(GrAtlasMgr* mgr, int offX, int offY, int width, int height, size_t bpp,
40 bool batchUploads) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000041 fRects = GrRectanizer::Factory(width, height);
42 fAtlasMgr = mgr;
43 fOffset.set(offX * width, offY * height);
44 fBytesPerPixel = bpp;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000045 fPlotData = NULL;
46 fDirtyRect.setEmpty();
47 fDirty = false;
48 fBatchUploads = batchUploads;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000049}
50
robertphillips@google.com8b169312013-10-15 17:47:36 +000051static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000052 loc->fX += offset.fX;
53 loc->fY += offset.fY;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000054}
55
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000056bool GrPlot::addSubImage(int width, int height, const void* image,
reed@google.comac10a2d2010-12-22 21:39:39 +000057 GrIPoint16* loc) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000058 float percentFull = fRects->percentFull();
commit-bot@chromium.orgf9529242014-02-14 18:41:47 +000059 if (!fRects->addRect(width, height, loc)) {
reed@google.comac10a2d2010-12-22 21:39:39 +000060 return false;
61 }
62
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000063 // if batching uploads, create backing memory on first use
64 // once the plot is nearly full we will revert to uploading each subimage individually
65 int plotWidth = fRects->width();
66 int plotHeight = fRects->height();
67 if (fBatchUploads && NULL == fPlotData && 0.0f == percentFull) {
68 fPlotData = SkNEW_ARRAY(unsigned char, fBytesPerPixel*plotWidth*plotHeight);
69 memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight);
70 }
71
72 // if we have backing memory, copy to the memory and set for future upload
73 if (NULL != fPlotData) {
74 const unsigned char* imagePtr = (const unsigned char*) image;
75 // point ourselves at the right starting spot
76 unsigned char* dataPtr = fPlotData;
77 dataPtr += fBytesPerPixel*plotWidth*loc->fY;
78 dataPtr += fBytesPerPixel*loc->fX;
79 // copy into the data buffer
80 for (int i = 0; i < height; ++i) {
81 memcpy(dataPtr, imagePtr, fBytesPerPixel*width);
82 dataPtr += fBytesPerPixel*plotWidth;
83 imagePtr += fBytesPerPixel*width;
84 }
85
86 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
87 adjust_for_offset(loc, fOffset);
88 fDirty = true;
89 // otherwise, just upload the image directly
90 } else {
91 adjust_for_offset(loc, fOffset);
92 GrContext* context = fTexture->getContext();
93 context->writeTexturePixels(fTexture,
94 loc->fX, loc->fY, width, height,
95 fTexture->config(), image, 0,
96 GrContext::kDontFlush_PixelOpsFlag);
97 }
reed@google.comac10a2d2010-12-22 21:39:39 +000098
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000099#if FONT_CACHE_STATS
100 ++g_UploadCount;
101#endif
102
reed@google.comac10a2d2010-12-22 21:39:39 +0000103 return true;
104}
105
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000106void GrPlot::uploadToTexture() {
107 static const float kNearlyFullTolerance = 0.85f;
108
109 // should only do this if batching is enabled
110 SkASSERT(fBatchUploads);
111
112 if (fDirty) {
113 SkASSERT(NULL != fTexture);
114 GrContext* context = fTexture->getContext();
115 // We pass the flag that does not force a flush. We assume our caller is
116 // smart and hasn't referenced the part of the texture we're about to update
117 // since the last flush.
118 int rowBytes = fBytesPerPixel*fRects->width();
119 const unsigned char* dataPtr = fPlotData;
120 dataPtr += rowBytes*fDirtyRect.fTop;
121 dataPtr += fBytesPerPixel*fDirtyRect.fLeft;
122 context->writeTexturePixels(fTexture,
skia.committer@gmail.coma1633da2014-05-15 03:03:58 +0000123 fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000124 fDirtyRect.width(), fDirtyRect.height(),
skia.committer@gmail.coma1633da2014-05-15 03:03:58 +0000125 fTexture->config(), dataPtr,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000126 rowBytes,
127 GrContext::kDontFlush_PixelOpsFlag);
128 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() {
140 SkASSERT(NULL != fRects);
141 fRects->reset();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000142}
143
reed@google.comac10a2d2010-12-22 21:39:39 +0000144///////////////////////////////////////////////////////////////////////////////
145
skia.committer@gmail.comc282ba82014-04-02 03:05:59 +0000146GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config,
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000147 const SkISize& backingTextureSize,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000148 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;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000151 fBackingTextureSize = backingTextureSize;
152 fNumPlotsX = numPlotsX;
153 fNumPlotsY = numPlotsY;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000154 fBatchUploads = batchUploads;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000155 fTexture = NULL;
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000156
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000157 int textureWidth = fBackingTextureSize.width();
158 int textureHeight = fBackingTextureSize.height();
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000159
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000160 int plotWidth = textureWidth / fNumPlotsX;
161 int plotHeight = textureHeight / fNumPlotsY;
162
163 SkASSERT(plotWidth * fNumPlotsX == textureWidth);
164 SkASSERT(plotHeight * fNumPlotsY == textureHeight);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000165
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000166 // set up allocated plots
robertphillips@google.com8b169312013-10-15 17:47:36 +0000167 size_t bpp = GrBytesPerPixel(fPixelConfig);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000168 fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000169
170 GrPlot* currPlot = fPlotArray;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000171 for (int y = numPlotsY-1; y >= 0; --y) {
172 for (int x = numPlotsX-1; x >= 0; --x) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000173 currPlot->init(this, x, y, plotWidth, plotHeight, bpp, batchUploads);
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000174
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000175 // build LRU list
176 fPlotList.addToHead(currPlot);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000177 ++currPlot;
178 }
179 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000180}
181
182GrAtlasMgr::~GrAtlasMgr() {
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000183 SkSafeUnref(fTexture);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000184 SkDELETE_ARRAY(fPlotArray);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000185
reed@google.comac10a2d2010-12-22 21:39:39 +0000186 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000187#if FONT_CACHE_STATS
188 GrPrintf("Num uploads: %d\n", g_UploadCount);
189#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000190}
191
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000192void GrAtlasMgr::moveToHead(GrPlot* plot) {
193 if (fPlotList.head() == plot) {
194 return;
195 }
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000196
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000197 fPlotList.remove(plot);
198 fPlotList.addToHead(plot);
199};
200
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000201GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas,
202 int width, int height, const void* image,
203 GrIPoint16* loc) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000204 // iterate through entire plot list for this atlas, see if we can find a hole
205 // last one was most recently added and probably most empty
206 for (int i = atlas->fPlots.count()-1; i >= 0; --i) {
207 GrPlot* plot = atlas->fPlots[i];
208 if (plot->addSubImage(width, height, image, loc)) {
209 this->moveToHead(plot);
210 return plot;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000211 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000212 }
213
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000214 // before we get a new plot, make sure we have a backing texture
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000215 if (NULL == fTexture) {
bsalomon@google.com95ed55a2013-01-24 14:46:47 +0000216 // TODO: Update this to use the cache rather than directly creating a texture.
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000217 GrTextureDesc desc;
218 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000219 desc.fWidth = fBackingTextureSize.width();
220 desc.fHeight = fBackingTextureSize.height();
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000221 desc.fConfig = fPixelConfig;
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000222
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000223 fTexture = fGpu->createTexture(desc, NULL, 0);
224 if (NULL == fTexture) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000225 return NULL;
226 }
227 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000228
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000229 // now look through all allocated plots for one we can share, in MRU order
230 GrPlotList::Iter plotIter;
231 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
232 GrPlot* plot;
233 while (NULL != (plot = plotIter.get())) {
234 // make sure texture is set for quick lookup
235 plot->fTexture = fTexture;
236 if (plot->addSubImage(width, height, image, loc)) {
237 this->moveToHead(plot);
238 // new plot for atlas, put at end of array
239 *(atlas->fPlots.append()) = plot;
240 return plot;
241 }
242 plotIter.next();
reed@google.comac10a2d2010-12-22 21:39:39 +0000243 }
244
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000245 // If the above fails, then the current plot list has no room
246 return NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +0000247}
248
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000249bool GrAtlasMgr::removePlot(GrAtlas* atlas, const GrPlot* plot) {
250 // iterate through plot list for this atlas
251 int count = atlas->fPlots.count();
252 for (int i = 0; i < count; ++i) {
253 if (plot == atlas->fPlots[i]) {
254 atlas->fPlots.remove(i);
255 return true;
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000256 }
257 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000258
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000259 return false;
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
263GrPlot* GrAtlasMgr::getUnusedPlot() {
264 GrPlotList::Iter plotIter;
265 plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
266 GrPlot* plot;
267 while (NULL != (plot = plotIter.get())) {
268 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
277void GrAtlasMgr::uploadPlotsToTexture() {
278 if (fBatchUploads) {
279 GrPlotList::Iter plotIter;
280 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
281 GrPlot* plot;
282 while (NULL != (plot = plotIter.get())) {
283 plot->uploadToTexture();
284 plotIter.next();
285 }
286 }
287}