1 2 /* 3 * 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. 7 */ 8 9 #include "GrAtlas.h" 10 #include "GrContext.h" 11 #include "GrGpu.h" 12 #include "GrRectanizer.h" 13 14 #if 0 15 #define GR_PLOT_WIDTH 8 16 #define GR_PLOT_HEIGHT 4 17 #define GR_ATLAS_WIDTH 256 18 #define GR_ATLAS_HEIGHT 256 19 20 #define GR_ATLAS_TEXTURE_WIDTH (GR_PLOT_WIDTH * GR_ATLAS_WIDTH) 21 #define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT) 22 23 #else 24 25 #define GR_ATLAS_TEXTURE_WIDTH 1024 26 #define GR_ATLAS_TEXTURE_HEIGHT 2048 27 28 #define GR_ATLAS_WIDTH 256 29 #define GR_ATLAS_HEIGHT 256 30 31 #define GR_PLOT_WIDTH (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH) 32 #define GR_PLOT_HEIGHT (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT) 33 34 #endif 35 36 /////////////////////////////////////////////////////////////////////////////// 37 38 #define BORDER 1 39 40 #ifdef SK_DEBUG 41 static int gCounter; 42 #endif 43 44 // for testing 45 #define FONT_CACHE_STATS 0 46 #if FONT_CACHE_STATS 47 static int g_UploadCount = 0; 48 #endif 49 50 GrPlot::GrPlot() : fDrawToken(NULL, 0) 51 , fNext(NULL) 52 , fTexture(NULL) 53 , fAtlasMgr(NULL) 54 , fBytesPerPixel(1) 55 { 56 fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER, 57 GR_ATLAS_HEIGHT - BORDER); 58 fOffset.set(0, 0); 59 } 60 61 GrPlot::~GrPlot() { 62 delete fRects; 63 } 64 65 static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) { 66 loc->fX += offset.fX * GR_ATLAS_WIDTH; 67 loc->fY += offset.fY * GR_ATLAS_HEIGHT; 68 } 69 70 static inline uint8_t* zero_fill(uint8_t* ptr, size_t count) { 71 sk_bzero(ptr, count); 72 return ptr + count; 73 } 74 75 bool GrPlot::addSubImage(int width, int height, const void* image, 76 GrIPoint16* loc) { 77 if (!fRects->addRect(width + BORDER, height + BORDER, loc)) { 78 return false; 79 } 80 81 SkAutoSMalloc<1024> storage; 82 int dstW = width + 2*BORDER; 83 int dstH = height + 2*BORDER; 84 if (BORDER) { 85 const size_t dstRB = dstW * fBytesPerPixel; 86 uint8_t* dst = (uint8_t*)storage.reset(dstH * dstRB); 87 sk_bzero(dst, dstRB); // zero top row 88 dst += dstRB; 89 for (int y = 0; y < height; y++) { 90 dst = zero_fill(dst, fBytesPerPixel); // zero left edge 91 memcpy(dst, image, width * fBytesPerPixel); 92 dst += width * fBytesPerPixel; 93 dst = zero_fill(dst, fBytesPerPixel); // zero right edge 94 image = (const void*)((const char*)image + width * fBytesPerPixel); 95 } 96 sk_bzero(dst, dstRB); // zero bottom row 97 image = storage.get(); 98 } 99 adjust_for_offset(loc, fOffset); 100 GrContext* context = fTexture->getContext(); 101 // We pass the flag that does not force a flush. We assume our caller is 102 // smart and hasn't referenced the part of the texture we're about to update 103 // since the last flush. 104 context->writeTexturePixels(fTexture, 105 loc->fX, loc->fY, dstW, dstH, 106 fTexture->config(), image, 0, 107 GrContext::kDontFlush_PixelOpsFlag); 108 109 // now tell the caller to skip the top/left BORDER 110 loc->fX += BORDER; 111 loc->fY += BORDER; 112 113 #if FONT_CACHE_STATS 114 ++g_UploadCount; 115 #endif 116 117 return true; 118 } 119 120 /////////////////////////////////////////////////////////////////////////////// 121 122 GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) { 123 fGpu = gpu; 124 fPixelConfig = config; 125 gpu->ref(); 126 fTexture = NULL; 127 128 // set up allocated plots 129 size_t bpp = GrBytesPerPixel(fPixelConfig); 130 fPlots = SkNEW_ARRAY(GrPlot, (GR_PLOT_WIDTH*GR_PLOT_HEIGHT)); 131 fFreePlots = NULL; 132 GrPlot* currPlot = fPlots; 133 for (int y = GR_PLOT_HEIGHT-1; y >= 0; --y) { 134 for (int x = GR_PLOT_WIDTH-1; x >= 0; --x) { 135 currPlot->fAtlasMgr = this; 136 currPlot->fOffset.set(x, y); 137 currPlot->fBytesPerPixel = bpp; 138 139 // add to free list 140 currPlot->fNext = fFreePlots; 141 fFreePlots = currPlot; 142 143 ++currPlot; 144 } 145 } 146 } 147 148 GrAtlasMgr::~GrAtlasMgr() { 149 SkSafeUnref(fTexture); 150 SkDELETE_ARRAY(fPlots); 151 152 fGpu->unref(); 153 #if FONT_CACHE_STATS 154 GrPrintf("Num uploads: %d\n", g_UploadCount); 155 #endif 156 } 157 158 GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas, 159 int width, int height, const void* image, 160 GrIPoint16* loc) { 161 // iterate through entire plot list, see if we can find a hole 162 GrPlot* plotIter = atlas->fPlots; 163 while (plotIter) { 164 if (plotIter->addSubImage(width, height, image, loc)) { 165 return plotIter; 166 } 167 plotIter = plotIter->fNext; 168 } 169 170 // If the above fails, then either we have no starting plot, or the current 171 // plot list is full. Either way we need to allocate a new plot 172 GrPlot* newPlot = this->allocPlot(); 173 if (NULL == newPlot) { 174 return NULL; 175 } 176 177 if (NULL == fTexture) { 178 // TODO: Update this to use the cache rather than directly creating a texture. 179 GrTextureDesc desc; 180 desc.fFlags = kDynamicUpdate_GrTextureFlagBit; 181 desc.fWidth = GR_ATLAS_TEXTURE_WIDTH; 182 desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT; 183 desc.fConfig = fPixelConfig; 184 185 fTexture = fGpu->createTexture(desc, NULL, 0); 186 if (NULL == fTexture) { 187 return NULL; 188 } 189 } 190 // be sure to set texture for fast lookup 191 newPlot->fTexture = fTexture; 192 193 if (!newPlot->addSubImage(width, height, image, loc)) { 194 this->freePlot(newPlot); 195 return NULL; 196 } 197 198 // new plot, put at head 199 newPlot->fNext = atlas->fPlots; 200 atlas->fPlots = newPlot; 201 202 return newPlot; 203 } 204 205 bool GrAtlasMgr::removeUnusedPlots(GrAtlas* atlas) { 206 207 // GrPlot** is used so that the head element can be easily 208 // modified when the first element is deleted 209 GrPlot** plotRef = &atlas->fPlots; 210 GrPlot* plot = atlas->fPlots; 211 bool removed = false; 212 while (NULL != plot) { 213 if (plot->drawToken().isIssued()) { 214 *plotRef = plot->fNext; 215 this->freePlot(plot); 216 plot = *plotRef; 217 removed = true; 218 } else { 219 plotRef = &plot->fNext; 220 plot = plot->fNext; 221 } 222 } 223 224 return removed; 225 } 226 227 void GrAtlasMgr::deletePlotList(GrPlot* plot) { 228 while (NULL != plot) { 229 GrPlot* next = plot->fNext; 230 this->freePlot(plot); 231 plot = next; 232 } 233 } 234 235 GrPlot* GrAtlasMgr::allocPlot() { 236 if (NULL == fFreePlots) { 237 return NULL; 238 } else { 239 GrPlot* alloc = fFreePlots; 240 fFreePlots = alloc->fNext; 241 #ifdef SK_DEBUG 242 // GrPrintf(" GrPlot %p [%d %d] %d\n", this, alloc->fOffset.fX, alloc->fOffset.fY, gCounter); 243 gCounter += 1; 244 #endif 245 return alloc; 246 } 247 248 } 249 250 void GrAtlasMgr::freePlot(GrPlot* plot) { 251 SkASSERT(this == plot->fAtlasMgr); 252 253 plot->fRects->reset(); 254 plot->fNext = fFreePlots; 255 fFreePlots = plot; 256 257 #ifdef SK_DEBUG 258 --gCounter; 259 // GrPrintf("~GrPlot %p [%d %d] %d\n", this, plot->fOffset.fX, plot->fOffset.fY, gCounter); 260 #endif 261 } 262