Home | History | Annotate | Download | only in gpu
      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     : fDrawToken(NULL, 0)
     25     , fID(-1)
     26     , fTexture(NULL)
     27     , fRects(NULL)
     28     , fAtlas(NULL)
     29     , fBytesPerPixel(1)
     30     , fDirty(false)
     31     , fBatchUploads(false)
     32 {
     33     fOffset.set(0, 0);
     34 }
     35 
     36 GrPlot::~GrPlot() {
     37     SkDELETE_ARRAY(fPlotData);
     38     fPlotData = NULL;
     39     delete fRects;
     40 }
     41 
     42 void GrPlot::init(GrAtlas* atlas, int id, int offX, int offY, int width, int height, size_t bpp,
     43                   bool batchUploads) {
     44     fID = id;
     45     fRects = GrRectanizer::Factory(width, height);
     46     fAtlas = atlas;
     47     fOffset.set(offX * width, offY * height);
     48     fBytesPerPixel = bpp;
     49     fPlotData = NULL;
     50     fDirtyRect.setEmpty();
     51     fDirty = false;
     52     fBatchUploads = batchUploads;
     53 }
     54 
     55 static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) {
     56     loc->fX += offset.fX;
     57     loc->fY += offset.fY;
     58 }
     59 
     60 bool GrPlot::addSubImage(int width, int height, const void* image, SkIPoint16* loc) {
     61     float percentFull = fRects->percentFull();
     62     if (!fRects->addRect(width, height, loc)) {
     63         return false;
     64     }
     65 
     66     // if batching uploads, create backing memory on first use
     67     // once the plot is nearly full we will revert to uploading each subimage individually
     68     int plotWidth = fRects->width();
     69     int plotHeight = fRects->height();
     70     if (fBatchUploads && NULL == fPlotData && 0.0f == percentFull) {
     71         fPlotData = SkNEW_ARRAY(unsigned char, fBytesPerPixel*plotWidth*plotHeight);
     72         memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight);
     73     }
     74 
     75     // if we have backing memory, copy to the memory and set for future upload
     76     if (fPlotData) {
     77         const unsigned char* imagePtr = (const unsigned char*) image;
     78         // point ourselves at the right starting spot
     79         unsigned char* dataPtr = fPlotData;
     80         dataPtr += fBytesPerPixel*plotWidth*loc->fY;
     81         dataPtr += fBytesPerPixel*loc->fX;
     82         // copy into the data buffer
     83         for (int i = 0; i < height; ++i) {
     84             memcpy(dataPtr, imagePtr, fBytesPerPixel*width);
     85             dataPtr += fBytesPerPixel*plotWidth;
     86             imagePtr += fBytesPerPixel*width;
     87         }
     88 
     89         fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height);
     90         adjust_for_offset(loc, fOffset);
     91         fDirty = true;
     92     // otherwise, just upload the image directly
     93     } else if (image) {
     94         adjust_for_offset(loc, fOffset);
     95         TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
     96         fTexture->writePixels(loc->fX, loc->fY, width, height, fTexture->config(), image, 0,
     97                               GrContext::kDontFlush_PixelOpsFlag);
     98     } else {
     99         adjust_for_offset(loc, fOffset);
    100     }
    101 
    102 #if FONT_CACHE_STATS
    103     ++g_UploadCount;
    104 #endif
    105 
    106     return true;
    107 }
    108 
    109 void GrPlot::uploadToTexture() {
    110     static const float kNearlyFullTolerance = 0.85f;
    111 
    112     // should only do this if batching is enabled
    113     SkASSERT(fBatchUploads);
    114 
    115     if (fDirty) {
    116         TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
    117         SkASSERT(fTexture);
    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.
    121         size_t rowBytes = fBytesPerPixel*fRects->width();
    122         const unsigned char* dataPtr = fPlotData;
    123         dataPtr += rowBytes*fDirtyRect.fTop;
    124         dataPtr += fBytesPerPixel*fDirtyRect.fLeft;
    125         fTexture->writePixels(fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
    126                               fDirtyRect.width(), fDirtyRect.height(), fTexture->config(), dataPtr,
    127                               rowBytes, 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 
    139 void GrPlot::resetRects() {
    140     SkASSERT(fRects);
    141     fRects->reset();
    142 }
    143 
    144 ///////////////////////////////////////////////////////////////////////////////
    145 
    146 GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrSurfaceFlags flags,
    147                  const SkISize& backingTextureSize,
    148                  int numPlotsX, int numPlotsY, bool batchUploads) {
    149     fGpu = SkRef(gpu);
    150     fPixelConfig = config;
    151     fFlags = flags;
    152     fBackingTextureSize = backingTextureSize;
    153     fNumPlotsX = numPlotsX;
    154     fNumPlotsY = numPlotsY;
    155     fBatchUploads = batchUploads;
    156     fTexture = NULL;
    157 
    158     int textureWidth = fBackingTextureSize.width();
    159     int textureHeight = fBackingTextureSize.height();
    160 
    161     int plotWidth = textureWidth / fNumPlotsX;
    162     int plotHeight = textureHeight / fNumPlotsY;
    163 
    164     SkASSERT(plotWidth * fNumPlotsX == textureWidth);
    165     SkASSERT(plotHeight * fNumPlotsY == textureHeight);
    166 
    167     // We currently do not support compressed atlases...
    168     SkASSERT(!GrPixelConfigIsCompressed(config));
    169 
    170     // set up allocated plots
    171     size_t bpp = GrBytesPerPixel(fPixelConfig);
    172     fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
    173 
    174     GrPlot* currPlot = fPlotArray;
    175     for (int y = numPlotsY-1; y >= 0; --y) {
    176         for (int x = numPlotsX-1; x >= 0; --x) {
    177             currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp, batchUploads);
    178 
    179             // build LRU list
    180             fPlotList.addToHead(currPlot);
    181             ++currPlot;
    182         }
    183     }
    184 }
    185 
    186 GrAtlas::~GrAtlas() {
    187     SkSafeUnref(fTexture);
    188     SkDELETE_ARRAY(fPlotArray);
    189 
    190     fGpu->unref();
    191 #if FONT_CACHE_STATS
    192       SkDebugf("Num uploads: %d\n", g_UploadCount);
    193 #endif
    194 }
    195 
    196 void GrAtlas::makeMRU(GrPlot* plot) {
    197     if (fPlotList.head() == plot) {
    198         return;
    199     }
    200 
    201     fPlotList.remove(plot);
    202     fPlotList.addToHead(plot);
    203 };
    204 
    205 GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage,
    206                             int width, int height, const void* image,
    207                             SkIPoint16* loc) {
    208     // iterate through entire plot list for this atlas, see if we can find a hole
    209     // last one was most recently added and probably most empty
    210     for (int i = usage->fPlots.count()-1; i >= 0; --i) {
    211         GrPlot* plot = usage->fPlots[i];
    212         // client may have plots from more than one atlas, must check for ours before adding
    213         if (this == plot->fAtlas && plot->addSubImage(width, height, image, loc)) {
    214             this->makeMRU(plot);
    215             return plot;
    216         }
    217     }
    218 
    219     // before we get a new plot, make sure we have a backing texture
    220     if (NULL == fTexture) {
    221         // TODO: Update this to use the cache rather than directly creating a texture.
    222         GrSurfaceDesc desc;
    223         desc.fFlags = fFlags;
    224         desc.fWidth = fBackingTextureSize.width();
    225         desc.fHeight = fBackingTextureSize.height();
    226         desc.fConfig = fPixelConfig;
    227 
    228         fTexture = fGpu->createTexture(desc, true, NULL, 0);
    229         if (NULL == fTexture) {
    230             return NULL;
    231         }
    232     }
    233 
    234     // now look through all allocated plots for one we can share, in MRU order
    235     GrPlotList::Iter plotIter;
    236     plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
    237     GrPlot* plot;
    238     while ((plot = plotIter.get())) {
    239         // make sure texture is set for quick lookup
    240         plot->fTexture = fTexture;
    241         if (plot->addSubImage(width, height, image, loc)) {
    242             this->makeMRU(plot);
    243             // new plot for atlas, put at end of array
    244             SkASSERT(!usage->fPlots.contains(plot));
    245             *(usage->fPlots.append()) = plot;
    246             return plot;
    247         }
    248         plotIter.next();
    249     }
    250 
    251     // If the above fails, then the current plot list has no room
    252     return NULL;
    253 }
    254 
    255 void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) {
    256     int index = usage->fPlots.find(const_cast<GrPlot*>(plot));
    257     if (index >= 0) {
    258         usage->fPlots.remove(index);
    259     }
    260 }
    261 
    262 // get a plot that's not being used by the current draw
    263 GrPlot* GrAtlas::getUnusedPlot() {
    264     GrPlotList::Iter plotIter;
    265     plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
    266     GrPlot* plot;
    267     while ((plot = plotIter.get())) {
    268         if (plot->drawToken().isIssued()) {
    269             return plot;
    270         }
    271         plotIter.prev();
    272     }
    273 
    274     return NULL;
    275 }
    276 
    277 void GrAtlas::uploadPlotsToTexture() {
    278     if (fBatchUploads) {
    279         GrPlotList::Iter plotIter;
    280         plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
    281         GrPlot* plot;
    282         while ((plot = plotIter.get())) {
    283             plot->uploadToTexture();
    284             plotIter.next();
    285         }
    286     }
    287 }
    288