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