blob: ea5ad50a39e5d11e5c630ebb6d2f5c4914ab2fdc [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
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000023GrPlot::GrPlot() : fDrawToken(NULL, 0)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000024 , fTexture(NULL)
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000025 , fRects(NULL)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000026 , fAtlasMgr(NULL)
27 , fBytesPerPixel(1)
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000028 , fDirty(false)
29 , fBatchUploads(false)
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000030{
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000031 fOffset.set(0, 0);
reed@google.comac10a2d2010-12-22 21:39:39 +000032}
33
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000034GrPlot::~GrPlot() {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000035 SkDELETE_ARRAY(fPlotData);
36 fPlotData = NULL;
reed@google.comac10a2d2010-12-22 21:39:39 +000037 delete fRects;
reed@google.comac10a2d2010-12-22 21:39:39 +000038}
39
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000040void GrPlot::init(GrAtlasMgr* mgr, int offX, int offY, int width, int height, size_t bpp,
41 bool batchUploads) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000042 fRects = GrRectanizer::Factory(width, height);
43 fAtlasMgr = mgr;
44 fOffset.set(offX * width, offY * height);
45 fBytesPerPixel = bpp;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000046 fPlotData = NULL;
47 fDirtyRect.setEmpty();
48 fDirty = false;
49 fBatchUploads = batchUploads;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000050}
51
robertphillipsd5373412014-06-02 10:20:14 -070052static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +000053 loc->fX += offset.fX;
54 loc->fY += offset.fY;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000055}
56
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +000057bool GrPlot::addSubImage(int width, int height, const void* image,
robertphillipsd5373412014-06-02 10:20:14 -070058 SkIPoint16* loc) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000059 float percentFull = fRects->percentFull();
commit-bot@chromium.orgf9529242014-02-14 18:41:47 +000060 if (!fRects->addRect(width, height, loc)) {
reed@google.comac10a2d2010-12-22 21:39:39 +000061 return false;
62 }
63
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000064 // if batching uploads, create backing memory on first use
65 // once the plot is nearly full we will revert to uploading each subimage individually
66 int plotWidth = fRects->width();
67 int plotHeight = fRects->height();
68 if (fBatchUploads && NULL == fPlotData && 0.0f == percentFull) {
69 fPlotData = SkNEW_ARRAY(unsigned char, fBytesPerPixel*plotWidth*plotHeight);
70 memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight);
71 }
72
73 // if we have backing memory, copy to the memory and set for future upload
74 if (NULL != fPlotData) {
75 const unsigned char* imagePtr = (const unsigned char*) image;
76 // point ourselves at the right starting spot
77 unsigned char* dataPtr = fPlotData;
78 dataPtr += fBytesPerPixel*plotWidth*loc->fY;
79 dataPtr += fBytesPerPixel*loc->fX;
80 // copy into the data buffer
81 for (int i = 0; i < height; ++i) {
82 memcpy(dataPtr, imagePtr, fBytesPerPixel*width);
83 dataPtr += fBytesPerPixel*plotWidth;
84 imagePtr += fBytesPerPixel*width;
85 }
86
87 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
88 adjust_for_offset(loc, fOffset);
89 fDirty = true;
90 // otherwise, just upload the image directly
91 } else {
92 adjust_for_offset(loc, fOffset);
93 GrContext* context = fTexture->getContext();
jvanverth8e80d172014-06-19 12:01:10 -070094 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +000095 context->writeTexturePixels(fTexture,
96 loc->fX, loc->fY, width, height,
97 fTexture->config(), image, 0,
98 GrContext::kDontFlush_PixelOpsFlag);
99 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000100
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000101#if FONT_CACHE_STATS
102 ++g_UploadCount;
103#endif
104
reed@google.comac10a2d2010-12-22 21:39:39 +0000105 return true;
106}
107
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000108void GrPlot::uploadToTexture() {
109 static const float kNearlyFullTolerance = 0.85f;
110
111 // should only do this if batching is enabled
112 SkASSERT(fBatchUploads);
113
114 if (fDirty) {
jvanverth8e80d172014-06-19 12:01:10 -0700115 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000116 SkASSERT(NULL != fTexture);
117 GrContext* context = fTexture->getContext();
118 // 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;
125 context->writeTexturePixels(fTexture,
skia.committer@gmail.coma1633da2014-05-15 03:03:58 +0000126 fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000127 fDirtyRect.width(), fDirtyRect.height(),
skia.committer@gmail.coma1633da2014-05-15 03:03:58 +0000128 fTexture->config(), dataPtr,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000129 rowBytes,
130 GrContext::kDontFlush_PixelOpsFlag);
131 fDirtyRect.setEmpty();
132 fDirty = false;
133 // If the Plot is nearly full, anything else we add will probably be small and one
134 // at a time, so free up the memory and after this upload any new images directly.
135 if (fRects->percentFull() > kNearlyFullTolerance) {
136 SkDELETE_ARRAY(fPlotData);
137 fPlotData = NULL;
138 }
139 }
140}
141
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000142void GrPlot::resetRects() {
143 SkASSERT(NULL != fRects);
144 fRects->reset();
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000145}
146
reed@google.comac10a2d2010-12-22 21:39:39 +0000147///////////////////////////////////////////////////////////////////////////////
148
skia.committer@gmail.comc282ba82014-04-02 03:05:59 +0000149GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config,
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000150 const SkISize& backingTextureSize,
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000151 int numPlotsX, int numPlotsY, bool batchUploads) {
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000152 fGpu = SkRef(gpu);
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000153 fPixelConfig = config;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000154 fBackingTextureSize = backingTextureSize;
155 fNumPlotsX = numPlotsX;
156 fNumPlotsY = numPlotsY;
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000157 fBatchUploads = batchUploads;
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000158 fTexture = NULL;
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000159
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000160 int textureWidth = fBackingTextureSize.width();
161 int textureHeight = fBackingTextureSize.height();
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000162
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000163 int plotWidth = textureWidth / fNumPlotsX;
164 int plotHeight = textureHeight / fNumPlotsY;
165
166 SkASSERT(plotWidth * fNumPlotsX == textureWidth);
167 SkASSERT(plotHeight * fNumPlotsY == textureHeight);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000168
commit-bot@chromium.org6e7ddaa2014-05-30 13:55:58 +0000169 // We currently do not support compressed atlases...
170 SkASSERT(!GrPixelConfigIsCompressed(config));
171
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000172 // set up allocated plots
robertphillips@google.com8b169312013-10-15 17:47:36 +0000173 size_t bpp = GrBytesPerPixel(fPixelConfig);
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000174 fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000175
176 GrPlot* currPlot = fPlotArray;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000177 for (int y = numPlotsY-1; y >= 0; --y) {
178 for (int x = numPlotsX-1; x >= 0; --x) {
commit-bot@chromium.org7801faa2014-05-14 15:14:51 +0000179 currPlot->init(this, x, y, plotWidth, plotHeight, bpp, batchUploads);
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000180
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000181 // build LRU list
182 fPlotList.addToHead(currPlot);
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000183 ++currPlot;
184 }
185 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000186}
187
188GrAtlasMgr::~GrAtlasMgr() {
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000189 SkSafeUnref(fTexture);
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000190 SkDELETE_ARRAY(fPlotArray);
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000191
reed@google.comac10a2d2010-12-22 21:39:39 +0000192 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000193#if FONT_CACHE_STATS
194 GrPrintf("Num uploads: %d\n", g_UploadCount);
195#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000196}
197
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000198void GrAtlasMgr::moveToHead(GrPlot* plot) {
199 if (fPlotList.head() == plot) {
200 return;
201 }
skia.committer@gmail.comade9a342014-03-04 03:02:32 +0000202
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000203 fPlotList.remove(plot);
204 fPlotList.addToHead(plot);
205};
206
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000207GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas,
208 int width, int height, const void* image,
robertphillipsd5373412014-06-02 10:20:14 -0700209 SkIPoint16* loc) {
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000210 // iterate through entire plot list for this atlas, see if we can find a hole
211 // last one was most recently added and probably most empty
212 for (int i = atlas->fPlots.count()-1; i >= 0; --i) {
213 GrPlot* plot = atlas->fPlots[i];
214 if (plot->addSubImage(width, height, image, loc)) {
215 this->moveToHead(plot);
216 return plot;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000217 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000218 }
219
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000220 // before we get a new plot, make sure we have a backing texture
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000221 if (NULL == fTexture) {
bsalomon@google.com95ed55a2013-01-24 14:46:47 +0000222 // TODO: Update this to use the cache rather than directly creating a texture.
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000223 GrTextureDesc desc;
224 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
commit-bot@chromium.org53e1e4d2014-04-01 16:25:11 +0000225 desc.fWidth = fBackingTextureSize.width();
226 desc.fHeight = fBackingTextureSize.height();
commit-bot@chromium.org95294412013-09-26 15:28:40 +0000227 desc.fConfig = fPixelConfig;
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000228
commit-bot@chromium.org3fddf0e2013-09-26 12:57:19 +0000229 fTexture = fGpu->createTexture(desc, NULL, 0);
230 if (NULL == fTexture) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000231 return NULL;
232 }
233 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000234
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000235 // now look through all allocated plots for one we can share, in MRU order
236 GrPlotList::Iter plotIter;
237 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
238 GrPlot* plot;
239 while (NULL != (plot = plotIter.get())) {
240 // make sure texture is set for quick lookup
241 plot->fTexture = fTexture;
242 if (plot->addSubImage(width, height, image, loc)) {
243 this->moveToHead(plot);
244 // new plot for atlas, put at end of array
245 *(atlas->fPlots.append()) = plot;
246 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
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000255bool GrAtlasMgr::removePlot(GrAtlas* atlas, const GrPlot* plot) {
256 // iterate through plot list for this atlas
257 int count = atlas->fPlots.count();
258 for (int i = 0; i < count; ++i) {
259 if (plot == atlas->fPlots[i]) {
260 atlas->fPlots.remove(i);
261 return true;
commit-bot@chromium.org7d330eb2013-09-27 19:39:38 +0000262 }
263 }
skia.committer@gmail.com50df4d02013-09-28 07:01:33 +0000264
commit-bot@chromium.orgc9b2c882014-03-03 14:30:25 +0000265 return false;
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
269GrPlot* GrAtlasMgr::getUnusedPlot() {
270 GrPlotList::Iter plotIter;
271 plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
272 GrPlot* plot;
273 while (NULL != (plot = plotIter.get())) {
274 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
283void GrAtlasMgr::uploadPlotsToTexture() {
284 if (fBatchUploads) {
285 GrPlotList::Iter plotIter;
286 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
287 GrPlot* plot;
288 while (NULL != (plot = plotIter.get())) {
289 plot->uploadToTexture();
290 plotIter.next();
291 }
292 }
293}