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         GrContext* context = fTexture->getContext();
     96         TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
     97         context->writeTexturePixels(fTexture,
     98                                     loc->fX, loc->fY, width, height,
     99                                     fTexture->config(), image, 0,
    100                                     GrContext::kDontFlush_PixelOpsFlag);
    101     } else {
    102         adjust_for_offset(loc, fOffset);
    103     }
    104 
    105 #if FONT_CACHE_STATS
    106     ++g_UploadCount;
    107 #endif
    108 
    109     return true;
    110 }
    111 
    112 void GrPlot::uploadToTexture() {
    113     static const float kNearlyFullTolerance = 0.85f;
    114 
    115     // should only do this if batching is enabled
    116     SkASSERT(fBatchUploads);
    117 
    118     if (fDirty) {
    119         TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture");
    120         SkASSERT(fTexture);
    121         GrContext* context = fTexture->getContext();
    122         // We pass the flag that does not force a flush. We assume our caller is
    123         // smart and hasn't referenced the part of the texture we're about to update
    124         // since the last flush.
    125         size_t rowBytes = fBytesPerPixel*fRects->width();
    126         const unsigned char* dataPtr = fPlotData;
    127         dataPtr += rowBytes*fDirtyRect.fTop;
    128         dataPtr += fBytesPerPixel*fDirtyRect.fLeft;
    129         context->writeTexturePixels(fTexture,
    130                                     fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop,
    131                                     fDirtyRect.width(), fDirtyRect.height(),
    132                                     fTexture->config(), dataPtr,
    133                                     rowBytes,
    134                                     GrContext::kDontFlush_PixelOpsFlag);
    135         fDirtyRect.setEmpty();
    136         fDirty = false;
    137         // If the Plot is nearly full, anything else we add will probably be small and one
    138         // at a time, so free up the memory and after this upload any new images directly.
    139         if (fRects->percentFull() > kNearlyFullTolerance) {
    140             SkDELETE_ARRAY(fPlotData);
    141             fPlotData = NULL;
    142         }
    143     }
    144 }
    145 
    146 void GrPlot::resetRects() {
    147     SkASSERT(fRects);
    148     fRects->reset();
    149 }
    150 
    151 ///////////////////////////////////////////////////////////////////////////////
    152 
    153 GrAtlas::GrAtlas(GrGpu* gpu, GrPixelConfig config, GrTextureFlags flags,
    154                  const SkISize& backingTextureSize,
    155                  int numPlotsX, int numPlotsY, bool batchUploads) {
    156     fGpu = SkRef(gpu);
    157     fPixelConfig = config;
    158     fFlags = flags;
    159     fBackingTextureSize = backingTextureSize;
    160     fNumPlotsX = numPlotsX;
    161     fNumPlotsY = numPlotsY;
    162     fBatchUploads = batchUploads;
    163     fTexture = NULL;
    164 
    165     int textureWidth = fBackingTextureSize.width();
    166     int textureHeight = fBackingTextureSize.height();
    167 
    168     int plotWidth = textureWidth / fNumPlotsX;
    169     int plotHeight = textureHeight / fNumPlotsY;
    170 
    171     SkASSERT(plotWidth * fNumPlotsX == textureWidth);
    172     SkASSERT(plotHeight * fNumPlotsY == textureHeight);
    173 
    174     // We currently do not support compressed atlases...
    175     SkASSERT(!GrPixelConfigIsCompressed(config));
    176 
    177     // set up allocated plots
    178     size_t bpp = GrBytesPerPixel(fPixelConfig);
    179     fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY));
    180 
    181     GrPlot* currPlot = fPlotArray;
    182     for (int y = numPlotsY-1; y >= 0; --y) {
    183         for (int x = numPlotsX-1; x >= 0; --x) {
    184             currPlot->init(this, y*numPlotsX+x, x, y, plotWidth, plotHeight, bpp, batchUploads);
    185 
    186             // build LRU list
    187             fPlotList.addToHead(currPlot);
    188             ++currPlot;
    189         }
    190     }
    191 }
    192 
    193 GrAtlas::~GrAtlas() {
    194     SkSafeUnref(fTexture);
    195     SkDELETE_ARRAY(fPlotArray);
    196 
    197     fGpu->unref();
    198 #if FONT_CACHE_STATS
    199       GrPrintf("Num uploads: %d\n", g_UploadCount);
    200 #endif
    201 }
    202 
    203 void GrAtlas::makeMRU(GrPlot* plot) {
    204     if (fPlotList.head() == plot) {
    205         return;
    206     }
    207 
    208     fPlotList.remove(plot);
    209     fPlotList.addToHead(plot);
    210 };
    211 
    212 GrPlot* GrAtlas::addToAtlas(ClientPlotUsage* usage,
    213                             int width, int height, const void* image,
    214                             SkIPoint16* loc) {
    215     // iterate through entire plot list for this atlas, see if we can find a hole
    216     // last one was most recently added and probably most empty
    217     for (int i = usage->fPlots.count()-1; i >= 0; --i) {
    218         GrPlot* plot = usage->fPlots[i];
    219         if (plot->addSubImage(width, height, image, loc)) {
    220             this->makeMRU(plot);
    221             return plot;
    222         }
    223     }
    224 
    225     // before we get a new plot, make sure we have a backing texture
    226     if (NULL == fTexture) {
    227         // TODO: Update this to use the cache rather than directly creating a texture.
    228         GrTextureDesc desc;
    229         desc.fFlags = fFlags | kDynamicUpdate_GrTextureFlagBit;
    230         desc.fWidth = fBackingTextureSize.width();
    231         desc.fHeight = fBackingTextureSize.height();
    232         desc.fConfig = fPixelConfig;
    233 
    234         fTexture = fGpu->createTexture(desc, NULL, 0);
    235         if (NULL == fTexture) {
    236             return NULL;
    237         }
    238     }
    239 
    240     // now look through all allocated plots for one we can share, in MRU order
    241     GrPlotList::Iter plotIter;
    242     plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
    243     GrPlot* plot;
    244     while ((plot = plotIter.get())) {
    245         // make sure texture is set for quick lookup
    246         plot->fTexture = fTexture;
    247         if (plot->addSubImage(width, height, image, loc)) {
    248             this->makeMRU(plot);
    249             // new plot for atlas, put at end of array
    250             SkASSERT(!usage->fPlots.contains(plot));
    251             *(usage->fPlots.append()) = plot;
    252             return plot;
    253         }
    254         plotIter.next();
    255     }
    256 
    257     // If the above fails, then the current plot list has no room
    258     return NULL;
    259 }
    260 
    261 void GrAtlas::RemovePlot(ClientPlotUsage* usage, const GrPlot* plot) {
    262     int index = usage->fPlots.find(const_cast<GrPlot*>(plot));
    263     if (index >= 0) {
    264         usage->fPlots.remove(index);
    265     }
    266 }
    267 
    268 // get a plot that's not being used by the current draw
    269 GrPlot* GrAtlas::getUnusedPlot() {
    270     GrPlotList::Iter plotIter;
    271     plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart);
    272     GrPlot* plot;
    273     while ((plot = plotIter.get())) {
    274         if (plot->drawToken().isIssued()) {
    275             return plot;
    276         }
    277         plotIter.prev();
    278     }
    279 
    280     return NULL;
    281 }
    282 
    283 void GrAtlas::uploadPlotsToTexture() {
    284     if (fBatchUploads) {
    285         GrPlotList::Iter plotIter;
    286         plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart);
    287         GrPlot* plot;
    288         while ((plot = plotIter.get())) {
    289             plot->uploadToTexture();
    290             plotIter.next();
    291         }
    292     }
    293 }
    294