blob: f2daca11c5879340a0c9cb05e0b6724d80aafff6 [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
9
epoger@google.comec3ed6a2011-07-28 14:26:00 +000010
reed@google.comac10a2d2010-12-22 21:39:39 +000011#include "GrAtlas.h"
bsalomon@google.com6f379512011-11-16 20:36:03 +000012#include "GrContext.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000013#include "GrGpu.h"
reed@google.comac10a2d2010-12-22 21:39:39 +000014#include "GrRectanizer.h"
15#include "GrPlotMgr.h"
16
17#if 0
18#define GR_PLOT_WIDTH 8
19#define GR_PLOT_HEIGHT 4
20#define GR_ATLAS_WIDTH 256
21#define GR_ATLAS_HEIGHT 256
22
23#define GR_ATLAS_TEXTURE_WIDTH (GR_PLOT_WIDTH * GR_ATLAS_WIDTH)
24#define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT)
25
26#else
27
28#define GR_ATLAS_TEXTURE_WIDTH 1024
29#define GR_ATLAS_TEXTURE_HEIGHT 2048
30
31#define GR_ATLAS_WIDTH 341
32#define GR_ATLAS_HEIGHT 341
33
34#define GR_PLOT_WIDTH (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH)
35#define GR_PLOT_HEIGHT (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT)
36
37#endif
38
39///////////////////////////////////////////////////////////////////////////////
40
41#define BORDER 1
42
43#if GR_DEBUG
44 static int gCounter;
45#endif
46
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000047// for testing
48#define FONT_CACHE_STATS 0
49#if FONT_CACHE_STATS
50static int g_UploadCount = 0;
51#endif
52
reed@google.com98539c62011-03-15 15:40:16 +000053GrAtlas::GrAtlas(GrAtlasMgr* mgr, int plotX, int plotY, GrMaskFormat format) {
reed@google.comac10a2d2010-12-22 21:39:39 +000054 fAtlasMgr = mgr; // just a pointer, not an owner
55 fNext = NULL;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000056 fUsed = false;
57
reed@google.com759c16e2011-03-15 19:15:15 +000058 fTexture = mgr->getTexture(format); // we're not an owner, just a pointer
reed@google.comac10a2d2010-12-22 21:39:39 +000059 fPlot.set(plotX, plotY);
60
61 fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER,
62 GR_ATLAS_HEIGHT - BORDER);
63
reed@google.com98539c62011-03-15 15:40:16 +000064 fMaskFormat = format;
65
reed@google.comac10a2d2010-12-22 21:39:39 +000066#if GR_DEBUG
reed@google.com3ef80cf2011-07-05 19:09:47 +000067// GrPrintf(" GrAtlas %p [%d %d] %d\n", this, plotX, plotY, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +000068 gCounter += 1;
69#endif
70}
71
72GrAtlas::~GrAtlas() {
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000073 fAtlasMgr->freePlot(fMaskFormat, fPlot.fX, fPlot.fY);
reed@google.comac10a2d2010-12-22 21:39:39 +000074
75 delete fRects;
76
77#if GR_DEBUG
78 --gCounter;
reed@google.com3ef80cf2011-07-05 19:09:47 +000079// GrPrintf("~GrAtlas %p [%d %d] %d\n", this, fPlot.fX, fPlot.fY, gCounter);
reed@google.comac10a2d2010-12-22 21:39:39 +000080#endif
81}
82
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +000083bool GrAtlas::RemoveUnusedAtlases(GrAtlasMgr* atlasMgr, GrAtlas** startAtlas) {
84 // GrAtlas** is used so that a pointer to the head element can be passed in and
85 // modified when the first element is deleted
86 GrAtlas** atlasRef = startAtlas;
87 GrAtlas* atlas = *startAtlas;
88 bool removed = false;
89 while (NULL != atlas) {
90 if (!atlas->used()) {
91 *atlasRef = atlas->fNext;
92 atlasMgr->deleteAtlas(atlas);
93 atlas = *atlasRef;
94 removed = true;
95 } else {
96 atlasRef = &atlas->fNext;
97 atlas = atlas->fNext;
98 }
99 }
100
101 return removed;
102}
103
reed@google.comac10a2d2010-12-22 21:39:39 +0000104static void adjustForPlot(GrIPoint16* loc, const GrIPoint16& plot) {
105 loc->fX += plot.fX * GR_ATLAS_WIDTH;
106 loc->fY += plot.fY * GR_ATLAS_HEIGHT;
107}
108
reed@google.com98539c62011-03-15 15:40:16 +0000109static uint8_t* zerofill(uint8_t* ptr, int count) {
110 while (--count >= 0) {
111 *ptr++ = 0;
112 }
113 return ptr;
114}
115
reed@google.comac10a2d2010-12-22 21:39:39 +0000116bool GrAtlas::addSubImage(int width, int height, const void* image,
117 GrIPoint16* loc) {
118 if (!fRects->addRect(width + BORDER, height + BORDER, loc)) {
119 return false;
120 }
121
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000122 SkAutoSMalloc<1024> storage;
reed@google.com98539c62011-03-15 15:40:16 +0000123 int dstW = width + 2*BORDER;
124 int dstH = height + 2*BORDER;
reed@google.comac10a2d2010-12-22 21:39:39 +0000125 if (BORDER) {
reed@google.com98539c62011-03-15 15:40:16 +0000126 const int bpp = GrMaskFormatBytesPerPixel(fMaskFormat);
127 const size_t dstRB = dstW * bpp;
bsalomon@google.com7d4679a2011-09-02 22:06:24 +0000128 uint8_t* dst = (uint8_t*)storage.reset(dstH * dstRB);
reed@google.com98539c62011-03-15 15:40:16 +0000129 Gr_bzero(dst, dstRB); // zero top row
130 dst += dstRB;
reed@google.comac10a2d2010-12-22 21:39:39 +0000131 for (int y = 0; y < height; y++) {
reed@google.com98539c62011-03-15 15:40:16 +0000132 dst = zerofill(dst, bpp); // zero left edge
133 memcpy(dst, image, width * bpp);
134 dst += width * bpp;
135 dst = zerofill(dst, bpp); // zero right edge
136 image = (const void*)((const char*)image + width * bpp);
reed@google.comac10a2d2010-12-22 21:39:39 +0000137 }
reed@google.com98539c62011-03-15 15:40:16 +0000138 Gr_bzero(dst, dstRB); // zero bottom row
reed@google.comac10a2d2010-12-22 21:39:39 +0000139 image = storage.get();
140 }
141 adjustForPlot(loc, fPlot);
bsalomon@google.com6f379512011-11-16 20:36:03 +0000142 GrContext* context = fTexture->getContext();
bsalomon@google.com0342a852012-08-20 19:22:38 +0000143 // We pass the flag that does not force a flush. We assume our caller is
144 // smart and hasn't referenced the part of the texture we're about to update
145 // since the last flush.
146 context->writeTexturePixels(fTexture,
147 loc->fX, loc->fY, dstW, dstH,
148 fTexture->config(), image, 0,
149 GrContext::kDontFlush_PixelOpsFlag);
reed@google.comac10a2d2010-12-22 21:39:39 +0000150
151 // now tell the caller to skip the top/left BORDER
152 loc->fX += BORDER;
153 loc->fY += BORDER;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000154
155#if FONT_CACHE_STATS
156 ++g_UploadCount;
157#endif
158
reed@google.comac10a2d2010-12-22 21:39:39 +0000159 return true;
160}
161
162///////////////////////////////////////////////////////////////////////////////
163
164GrAtlasMgr::GrAtlasMgr(GrGpu* gpu) {
165 fGpu = gpu;
166 gpu->ref();
reed@google.com759c16e2011-03-15 19:15:15 +0000167 Gr_bzero(fTexture, sizeof(fTexture));
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000168 fPlotMgr = SkNEW_ARGS(GrPlotMgr, (GR_PLOT_WIDTH, GR_PLOT_HEIGHT));
reed@google.comac10a2d2010-12-22 21:39:39 +0000169}
170
171GrAtlasMgr::~GrAtlasMgr() {
reed@google.com759c16e2011-03-15 19:15:15 +0000172 for (size_t i = 0; i < GR_ARRAY_COUNT(fTexture); i++) {
173 GrSafeUnref(fTexture[i]);
174 }
reed@google.comac10a2d2010-12-22 21:39:39 +0000175 delete fPlotMgr;
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000176
reed@google.comac10a2d2010-12-22 21:39:39 +0000177 fGpu->unref();
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000178#if FONT_CACHE_STATS
179 GrPrintf("Num uploads: %d\n", g_UploadCount);
180#endif
reed@google.comac10a2d2010-12-22 21:39:39 +0000181}
182
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000183static GrPixelConfig maskformat2pixelconfig(GrMaskFormat format) {
reed@google.com98539c62011-03-15 15:40:16 +0000184 switch (format) {
185 case kA8_GrMaskFormat:
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000186 return kAlpha_8_GrPixelConfig;
reed@google.com98539c62011-03-15 15:40:16 +0000187 case kA565_GrMaskFormat:
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000188 return kRGB_565_GrPixelConfig;
caryclark@google.com1eeaf0b2011-06-22 13:19:43 +0000189 case kA888_GrMaskFormat:
bsalomon@google.comfec0bc32013-02-07 14:43:04 +0000190 return kSkia8888_GrPixelConfig;
reed@google.com98539c62011-03-15 15:40:16 +0000191 default:
192 GrAssert(!"unknown maskformat");
193 }
bsalomon@google.com669fdc42011-04-05 17:08:27 +0000194 return kUnknown_GrPixelConfig;
reed@google.com98539c62011-03-15 15:40:16 +0000195}
196
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000197GrAtlas* GrAtlasMgr::addToAtlas(GrAtlas** atlas,
reed@google.comac10a2d2010-12-22 21:39:39 +0000198 int width, int height, const void* image,
reed@google.com98539c62011-03-15 15:40:16 +0000199 GrMaskFormat format,
reed@google.comac10a2d2010-12-22 21:39:39 +0000200 GrIPoint16* loc) {
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000201 GrAssert(NULL == *atlas || (*atlas)->getMaskFormat() == format);
reed@google.com759c16e2011-03-15 19:15:15 +0000202
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000203 // iterate through entire atlas list, see if we can find a hole
204 GrAtlas* atlasIter = *atlas;
205 while (atlasIter) {
206 if (atlasIter->addSubImage(width, height, image, loc)) {
207 return atlasIter;
208 }
209 atlasIter = atlasIter->fNext;
reed@google.comac10a2d2010-12-22 21:39:39 +0000210 }
211
212 // If the above fails, then either we have no starting atlas, or the current
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000213 // atlas list is full. Either way we need to allocate a new atlas
reed@google.comac10a2d2010-12-22 21:39:39 +0000214
215 GrIPoint16 plot;
216 if (!fPlotMgr->newPlot(&plot)) {
217 return NULL;
218 }
219
reed@google.com759c16e2011-03-15 19:15:15 +0000220 GrAssert(0 == kA8_GrMaskFormat);
221 GrAssert(1 == kA565_GrMaskFormat);
222 if (NULL == fTexture[format]) {
bsalomon@google.com95ed55a2013-01-24 14:46:47 +0000223 // TODO: Update this to use the cache rather than directly creating a texture.
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000224 GrTextureDesc desc;
225 desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
226 desc.fWidth = GR_ATLAS_TEXTURE_WIDTH;
227 desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT;
228 desc.fConfig = maskformat2pixelconfig(format);
229
reed@google.com759c16e2011-03-15 19:15:15 +0000230 fTexture[format] = fGpu->createTexture(desc, NULL, 0);
231 if (NULL == fTexture[format]) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000232 return NULL;
233 }
234 }
235
tomhudson@google.comc377baf2012-07-09 20:17:56 +0000236 GrAtlas* newAtlas = SkNEW_ARGS(GrAtlas, (this, plot.fX, plot.fY, format));
reed@google.comac10a2d2010-12-22 21:39:39 +0000237 if (!newAtlas->addSubImage(width, height, image, loc)) {
238 delete newAtlas;
239 return NULL;
240 }
241
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000242 // new atlas, put at head
243 newAtlas->fNext = *atlas;
244 *atlas = newAtlas;
245
reed@google.comac10a2d2010-12-22 21:39:39 +0000246 return newAtlas;
247}
248
commit-bot@chromium.org67ed64e2013-08-05 19:42:56 +0000249void GrAtlasMgr::freePlot(GrMaskFormat format, int x, int y) {
reed@google.comac10a2d2010-12-22 21:39:39 +0000250 GrAssert(fPlotMgr->isBusy(x, y));
251 fPlotMgr->freePlot(x, y);
252}