OLD | NEW |
(Empty) | |
| 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 #include "GrTracing.h" |
| 14 |
| 15 /////////////////////////////////////////////////////////////////////////////// |
| 16 |
| 17 // for testing |
| 18 #define FONT_CACHE_STATS 0 |
| 19 #if FONT_CACHE_STATS |
| 20 static int g_UploadCount = 0; |
| 21 #endif |
| 22 |
| 23 GrPlot::GrPlot() |
| 24 : fID(-1) |
| 25 , fTexture(nullptr) |
| 26 , fRects(nullptr) |
| 27 , fAtlas(nullptr) |
| 28 , fBytesPerPixel(1) |
| 29 , fDirty(false) |
| 30 , fBatchUploads(false) |
| 31 { |
| 32 fOffset.set(0, 0); |
| 33 } |
| 34 |
| 35 GrPlot::~GrPlot() { |
| 36 delete[] fPlotData; |
| 37 fPlotData = nullptr; |
| 38 delete fRects; |
| 39 } |
| 40 |
| 41 void GrPlot::init(GrAtlas* atlas, int id, int offX, int offY, int width, int hei
ght, size_t bpp, |
| 42 bool batchUploads) { |
| 43 fID = id; |
| 44 fRects = GrRectanizer::Factory(width, height); |
| 45 fAtlas = atlas; |
| 46 fOffset.set(offX * width, offY * height); |
| 47 fBytesPerPixel = bpp; |
| 48 fPlotData = nullptr; |
| 49 fDirtyRect.setEmpty(); |
| 50 fDirty = false; |
| 51 fBatchUploads = batchUploads; |
| 52 } |
| 53 |
| 54 static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset)
{ |
| 55 loc->fX += offset.fX; |
| 56 loc->fY += offset.fY; |
| 57 } |
| 58 |
| 59 bool GrPlot::addSubImage(int width, int height, const void* image, SkIPoint16* l
oc) { |
| 60 float percentFull = fRects->percentFull(); |
| 61 if (!fRects->addRect(width, height, loc)) { |
| 62 return false; |
| 63 } |
| 64 |
| 65 // if batching uploads, create backing memory on first use |
| 66 // once the plot is nearly full we will revert to uploading each subimage in
dividually |
| 67 int plotWidth = fRects->width(); |
| 68 int plotHeight = fRects->height(); |
| 69 if (fBatchUploads && nullptr == fPlotData && 0.0f == percentFull) { |
| 70 fPlotData = new unsigned char[fBytesPerPixel * plotWidth * plotHeight]; |
| 71 memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight); |
| 72 } |
| 73 |
| 74 // if we have backing memory, copy to the memory and set for future upload |
| 75 if (fPlotData) { |
| 76 const unsigned char* imagePtr = (const unsigned char*) image; |
| 77 // point ourselves at the right starting spot |
| 78 unsigned char* dataPtr = fPlotData; |
| 79 dataPtr += fBytesPerPixel*plotWidth*loc->fY; |
| 80 dataPtr += fBytesPerPixel*loc->fX; |
| 81 // copy into the data buffer |
| 82 for (int i = 0; i < height; ++i) { |
| 83 memcpy(dataPtr, imagePtr, fBytesPerPixel*width); |
| 84 dataPtr += fBytesPerPixel*plotWidth; |
| 85 imagePtr += fBytesPerPixel*width; |
| 86 } |
| 87 |
| 88 fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height); |
| 89 adjust_for_offset(loc, fOffset); |
| 90 fDirty = true; |
| 91 // otherwise, just upload the image directly |
| 92 } else if (image) { |
| 93 adjust_for_offset(loc, fOffset); |
| 94 TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTex
ture"); |
| 95 fTexture->writePixels(NULL, loc->fX, loc->fY, width, height, fTexture->c
onfig(), image, 0, |
| 96 GrContext::kDontFlush_PixelOpsFlag); |
| 97 } else { |
| 98 adjust_for_offset(loc, fOffset); |
| 99 } |
| 100 |
| 101 #if FONT_CACHE_STATS |
| 102 ++g_UploadCount; |
| 103 #endif |
| 104 |
| 105 return true; |
| 106 } |
| 107 |
| 108 void GrPlot::resetRects() { |
| 109 SkASSERT(fRects); |
| 110 fRects->reset(); |
| 111 } |
| 112 |
| 113 /////////////////////////////////////////////////////////////////////////////// |
| 114 |
| 115 GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrSurfaceFlags flags, |
| 116 const SkISize& backingTextureSize, |
| 117 int numPlotsX, int numPlotsY, bool batchUploads) { |
| 118 fGpu = SkRef(gpu); |
| 119 fPixelConfig = config; |
| 120 fFlags = flags; |
| 121 fBackingTextureSize = backingTextureSize; |
| 122 fNumPlotsX = numPlotsX; |
| 123 fNumPlotsY = numPlotsY; |
| 124 fBatchUploads = batchUploads; |
| 125 fTexture = nullptr; |
| 126 |
| 127 int textureWidth = fBackingTextureSize.width(); |
| 128 int textureHeight = fBackingTextureSize.height(); |
| 129 |
| 130 int plotWidth = textureWidth / fNumPlotsX; |
| 131 int plotHeight = textureHeight / fNumPlotsY; |
| 132 |
| 133 SkASSERT(plotWidth * fNumPlotsX == textureWidth); |
| 134 SkASSERT(plotHeight * fNumPlotsY == textureHeight); |
| 135 |
| 136 // We currently do not support compressed atlases... |
| 137 SkASSERT(!GrPixelConfigIsCompressed(config)); |
| 138 |
| 139 // set up allocated plots |
| 140 size_t bpp = GrBytesPerPixel(fPixelConfig); |
| 141 fPlotArray = new GrPlot[(fNumPlotsX * fNumPlotsY)]; |
| 142 |
| 143 GrPlot* currPlot = fPlotArray; |
| 144 for (int y = numPlotsY-1; y >= 0; --y) { |
| 145 for (int x = numPlotsX-1; x >= 0; --x) { |
| 146 currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp
, batchUploads); |
| 147 |
| 148 // build LRU list |
| 149 fPlotList.addToHead(currPlot); |
| 150 ++currPlot; |
| 151 } |
| 152 } |
| 153 } |
| 154 |
| 155 GrAtlas::~GrAtlas() { |
| 156 SkSafeUnref(fTexture); |
| 157 delete[] fPlotArray; |
| 158 |
| 159 fGpu->unref(); |
| 160 #if FONT_CACHE_STATS |
| 161 SkDebugf("Num uploads: %d\n", g_UploadCount); |
| 162 #endif |
| 163 } |
| 164 |
| 165 void GrAtlas::makeMRU(GrPlot* plot) { |
| 166 if (fPlotList.head() == plot) { |
| 167 return; |
| 168 } |
| 169 |
| 170 fPlotList.remove(plot); |
| 171 fPlotList.addToHead(plot); |
| 172 }; |
| 173 |
| 174 GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage, |
| 175 int width, int height, const void* image, |
| 176 SkIPoint16* loc) { |
| 177 // iterate through entire plot list for this atlas, see if we can find a hol
e |
| 178 // last one was most recently added and probably most empty |
| 179 for (int i = usage->fPlots.count()-1; i >= 0; --i) { |
| 180 GrPlot* plot = usage->fPlots[i]; |
| 181 // client may have plots from more than one atlas, must check for ours b
efore adding |
| 182 if (this == plot->fAtlas && plot->addSubImage(width, height, image, loc)
) { |
| 183 this->makeMRU(plot); |
| 184 return plot; |
| 185 } |
| 186 } |
| 187 |
| 188 // before we get a new plot, make sure we have a backing texture |
| 189 if (nullptr == fTexture) { |
| 190 // TODO: Update this to use the cache rather than directly creating a te
xture. |
| 191 GrSurfaceDesc desc; |
| 192 desc.fFlags = fFlags; |
| 193 desc.fWidth = fBackingTextureSize.width(); |
| 194 desc.fHeight = fBackingTextureSize.height(); |
| 195 desc.fConfig = fPixelConfig; |
| 196 |
| 197 fTexture = fGpu->createTexture(desc, true, nullptr, 0); |
| 198 if (nullptr == fTexture) { |
| 199 return nullptr; |
| 200 } |
| 201 } |
| 202 |
| 203 // now look through all allocated plots for one we can share, in MRU order |
| 204 GrPlotList::Iter plotIter; |
| 205 plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart); |
| 206 GrPlot* plot; |
| 207 while ((plot = plotIter.get())) { |
| 208 // make sure texture is set for quick lookup |
| 209 plot->fTexture = fTexture; |
| 210 if (plot->addSubImage(width, height, image, loc)) { |
| 211 this->makeMRU(plot); |
| 212 // new plot for atlas, put at end of array |
| 213 SkASSERT(!usage->fPlots.contains(plot)); |
| 214 *(usage->fPlots.append()) = plot; |
| 215 return plot; |
| 216 } |
| 217 plotIter.next(); |
| 218 } |
| 219 |
| 220 // If the above fails, then the current plot list has no room |
| 221 return nullptr; |
| 222 } |
| 223 |
| 224 void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) { |
| 225 int index = usage->fPlots.find(const_cast<GrPlot*>(plot)); |
| 226 if (index >= 0) { |
| 227 usage->fPlots.remove(index); |
| 228 } |
| 229 } |
OLD | NEW |