diff options
Diffstat (limited to 'chromium/third_party/skia/src/gpu/GrAtlas.cpp')
-rw-r--r-- | chromium/third_party/skia/src/gpu/GrAtlas.cpp | 346 |
1 files changed, 189 insertions, 157 deletions
diff --git a/chromium/third_party/skia/src/gpu/GrAtlas.cpp b/chromium/third_party/skia/src/gpu/GrAtlas.cpp index dd362908d3f..ea5ad50a39e 100644 --- a/chromium/third_party/skia/src/gpu/GrAtlas.cpp +++ b/chromium/third_party/skia/src/gpu/GrAtlas.cpp @@ -10,37 +10,10 @@ #include "GrContext.h" #include "GrGpu.h" #include "GrRectanizer.h" - -#if 0 -#define GR_PLOT_WIDTH 8 -#define GR_PLOT_HEIGHT 4 -#define GR_ATLAS_WIDTH 256 -#define GR_ATLAS_HEIGHT 256 - -#define GR_ATLAS_TEXTURE_WIDTH (GR_PLOT_WIDTH * GR_ATLAS_WIDTH) -#define GR_ATLAS_TEXTURE_HEIGHT (GR_PLOT_HEIGHT * GR_ATLAS_HEIGHT) - -#else - -#define GR_ATLAS_TEXTURE_WIDTH 1024 -#define GR_ATLAS_TEXTURE_HEIGHT 2048 - -#define GR_ATLAS_WIDTH 256 -#define GR_ATLAS_HEIGHT 256 - -#define GR_PLOT_WIDTH (GR_ATLAS_TEXTURE_WIDTH / GR_ATLAS_WIDTH) -#define GR_PLOT_HEIGHT (GR_ATLAS_TEXTURE_HEIGHT / GR_ATLAS_HEIGHT) - -#endif +#include "GrTracing.h" /////////////////////////////////////////////////////////////////////////////// -#define BORDER 1 - -#ifdef SK_DEBUG - static int gCounter; -#endif - // for testing #define FONT_CACHE_STATS 0 #if FONT_CACHE_STATS @@ -48,67 +21,82 @@ static int g_UploadCount = 0; #endif GrPlot::GrPlot() : fDrawToken(NULL, 0) - , fNext(NULL) , fTexture(NULL) + , fRects(NULL) , fAtlasMgr(NULL) , fBytesPerPixel(1) + , fDirty(false) + , fBatchUploads(false) { - fRects = GrRectanizer::Factory(GR_ATLAS_WIDTH - BORDER, - GR_ATLAS_HEIGHT - BORDER); fOffset.set(0, 0); } GrPlot::~GrPlot() { + SkDELETE_ARRAY(fPlotData); + fPlotData = NULL; delete fRects; } -static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) { - loc->fX += offset.fX * GR_ATLAS_WIDTH; - loc->fY += offset.fY * GR_ATLAS_HEIGHT; +void GrPlot::init(GrAtlasMgr* mgr, int offX, int offY, int width, int height, size_t bpp, + bool batchUploads) { + fRects = GrRectanizer::Factory(width, height); + fAtlasMgr = mgr; + fOffset.set(offX * width, offY * height); + fBytesPerPixel = bpp; + fPlotData = NULL; + fDirtyRect.setEmpty(); + fDirty = false; + fBatchUploads = batchUploads; } -static inline uint8_t* zero_fill(uint8_t* ptr, size_t count) { - sk_bzero(ptr, count); - return ptr + count; +static inline void adjust_for_offset(SkIPoint16* loc, const SkIPoint16& offset) { + loc->fX += offset.fX; + loc->fY += offset.fY; } bool GrPlot::addSubImage(int width, int height, const void* image, - GrIPoint16* loc) { - if (!fRects->addRect(width + BORDER, height + BORDER, loc)) { + SkIPoint16* loc) { + float percentFull = fRects->percentFull(); + if (!fRects->addRect(width, height, loc)) { return false; } - SkAutoSMalloc<1024> storage; - int dstW = width + 2*BORDER; - int dstH = height + 2*BORDER; - if (BORDER) { - const size_t dstRB = dstW * fBytesPerPixel; - uint8_t* dst = (uint8_t*)storage.reset(dstH * dstRB); - sk_bzero(dst, dstRB); // zero top row - dst += dstRB; - for (int y = 0; y < height; y++) { - dst = zero_fill(dst, fBytesPerPixel); // zero left edge - memcpy(dst, image, width * fBytesPerPixel); - dst += width * fBytesPerPixel; - dst = zero_fill(dst, fBytesPerPixel); // zero right edge - image = (const void*)((const char*)image + width * fBytesPerPixel); + // if batching uploads, create backing memory on first use + // once the plot is nearly full we will revert to uploading each subimage individually + int plotWidth = fRects->width(); + int plotHeight = fRects->height(); + if (fBatchUploads && NULL == fPlotData && 0.0f == percentFull) { + fPlotData = SkNEW_ARRAY(unsigned char, fBytesPerPixel*plotWidth*plotHeight); + memset(fPlotData, 0, fBytesPerPixel*plotWidth*plotHeight); + } + + // if we have backing memory, copy to the memory and set for future upload + if (NULL != fPlotData) { + const unsigned char* imagePtr = (const unsigned char*) image; + // point ourselves at the right starting spot + unsigned char* dataPtr = fPlotData; + dataPtr += fBytesPerPixel*plotWidth*loc->fY; + dataPtr += fBytesPerPixel*loc->fX; + // copy into the data buffer + for (int i = 0; i < height; ++i) { + memcpy(dataPtr, imagePtr, fBytesPerPixel*width); + dataPtr += fBytesPerPixel*plotWidth; + imagePtr += fBytesPerPixel*width; } - sk_bzero(dst, dstRB); // zero bottom row - image = storage.get(); + + fDirtyRect.join(loc->fX, loc->fY, loc->fX + width, loc->fY + height); + adjust_for_offset(loc, fOffset); + fDirty = true; + // otherwise, just upload the image directly + } else { + adjust_for_offset(loc, fOffset); + GrContext* context = fTexture->getContext(); + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture"); + context->writeTexturePixels(fTexture, + loc->fX, loc->fY, width, height, + fTexture->config(), image, 0, + GrContext::kDontFlush_PixelOpsFlag); } - adjust_for_offset(loc, fOffset); - GrContext* context = fTexture->getContext(); - // We pass the flag that does not force a flush. We assume our caller is - // smart and hasn't referenced the part of the texture we're about to update - // since the last flush. - context->writeTexturePixels(fTexture, - loc->fX, loc->fY, dstW, dstH, - fTexture->config(), image, 0, - GrContext::kDontFlush_PixelOpsFlag); - - // now tell the caller to skip the top/left BORDER - loc->fX += BORDER; - loc->fY += BORDER; #if FONT_CACHE_STATS ++g_UploadCount; @@ -117,29 +105,81 @@ bool GrPlot::addSubImage(int width, int height, const void* image, return true; } +void GrPlot::uploadToTexture() { + static const float kNearlyFullTolerance = 0.85f; + + // should only do this if batching is enabled + SkASSERT(fBatchUploads); + + if (fDirty) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), "GrPlot::uploadToTexture"); + SkASSERT(NULL != fTexture); + GrContext* context = fTexture->getContext(); + // We pass the flag that does not force a flush. We assume our caller is + // smart and hasn't referenced the part of the texture we're about to update + // since the last flush. + size_t rowBytes = fBytesPerPixel*fRects->width(); + const unsigned char* dataPtr = fPlotData; + dataPtr += rowBytes*fDirtyRect.fTop; + dataPtr += fBytesPerPixel*fDirtyRect.fLeft; + context->writeTexturePixels(fTexture, + fOffset.fX + fDirtyRect.fLeft, fOffset.fY + fDirtyRect.fTop, + fDirtyRect.width(), fDirtyRect.height(), + fTexture->config(), dataPtr, + rowBytes, + GrContext::kDontFlush_PixelOpsFlag); + fDirtyRect.setEmpty(); + fDirty = false; + // If the Plot is nearly full, anything else we add will probably be small and one + // at a time, so free up the memory and after this upload any new images directly. + if (fRects->percentFull() > kNearlyFullTolerance) { + SkDELETE_ARRAY(fPlotData); + fPlotData = NULL; + } + } +} + +void GrPlot::resetRects() { + SkASSERT(NULL != fRects); + fRects->reset(); +} + /////////////////////////////////////////////////////////////////////////////// -GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) { - fGpu = gpu; +GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config, + const SkISize& backingTextureSize, + int numPlotsX, int numPlotsY, bool batchUploads) { + fGpu = SkRef(gpu); fPixelConfig = config; - gpu->ref(); + fBackingTextureSize = backingTextureSize; + fNumPlotsX = numPlotsX; + fNumPlotsY = numPlotsY; + fBatchUploads = batchUploads; fTexture = NULL; + int textureWidth = fBackingTextureSize.width(); + int textureHeight = fBackingTextureSize.height(); + + int plotWidth = textureWidth / fNumPlotsX; + int plotHeight = textureHeight / fNumPlotsY; + + SkASSERT(plotWidth * fNumPlotsX == textureWidth); + SkASSERT(plotHeight * fNumPlotsY == textureHeight); + + // We currently do not support compressed atlases... + SkASSERT(!GrPixelConfigIsCompressed(config)); + // set up allocated plots size_t bpp = GrBytesPerPixel(fPixelConfig); - fPlots = SkNEW_ARRAY(GrPlot, (GR_PLOT_WIDTH*GR_PLOT_HEIGHT)); - fFreePlots = NULL; - GrPlot* currPlot = fPlots; - for (int y = GR_PLOT_HEIGHT-1; y >= 0; --y) { - for (int x = GR_PLOT_WIDTH-1; x >= 0; --x) { - currPlot->fAtlasMgr = this; - currPlot->fOffset.set(x, y); - currPlot->fBytesPerPixel = bpp; - - // add to free list - currPlot->fNext = fFreePlots; - fFreePlots = currPlot; + fPlotArray = SkNEW_ARRAY(GrPlot, (fNumPlotsX*fNumPlotsY)); + + GrPlot* currPlot = fPlotArray; + for (int y = numPlotsY-1; y >= 0; --y) { + for (int x = numPlotsX-1; x >= 0; --x) { + currPlot->init(this, x, y, plotWidth, plotHeight, bpp, batchUploads); + // build LRU list + fPlotList.addToHead(currPlot); ++currPlot; } } @@ -147,7 +187,7 @@ GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config) { GrAtlasMgr::~GrAtlasMgr() { SkSafeUnref(fTexture); - SkDELETE_ARRAY(fPlots); + SkDELETE_ARRAY(fPlotArray); fGpu->unref(); #if FONT_CACHE_STATS @@ -155,31 +195,35 @@ GrAtlasMgr::~GrAtlasMgr() { #endif } +void GrAtlasMgr::moveToHead(GrPlot* plot) { + if (fPlotList.head() == plot) { + return; + } + + fPlotList.remove(plot); + fPlotList.addToHead(plot); +}; + GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas, int width, int height, const void* image, - GrIPoint16* loc) { - // iterate through entire plot list, see if we can find a hole - GrPlot* plotIter = atlas->fPlots; - while (plotIter) { - if (plotIter->addSubImage(width, height, image, loc)) { - return plotIter; + SkIPoint16* loc) { + // iterate through entire plot list for this atlas, see if we can find a hole + // last one was most recently added and probably most empty + for (int i = atlas->fPlots.count()-1; i >= 0; --i) { + GrPlot* plot = atlas->fPlots[i]; + if (plot->addSubImage(width, height, image, loc)) { + this->moveToHead(plot); + return plot; } - plotIter = plotIter->fNext; - } - - // If the above fails, then either we have no starting plot, or the current - // plot list is full. Either way we need to allocate a new plot - GrPlot* newPlot = this->allocPlot(); - if (NULL == newPlot) { - return NULL; } + // before we get a new plot, make sure we have a backing texture if (NULL == fTexture) { // TODO: Update this to use the cache rather than directly creating a texture. GrTextureDesc desc; desc.fFlags = kDynamicUpdate_GrTextureFlagBit; - desc.fWidth = GR_ATLAS_TEXTURE_WIDTH; - desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT; + desc.fWidth = fBackingTextureSize.width(); + desc.fHeight = fBackingTextureSize.height(); desc.fConfig = fPixelConfig; fTexture = fGpu->createTexture(desc, NULL, 0); @@ -187,75 +231,63 @@ GrPlot* GrAtlasMgr::addToAtlas(GrAtlas* atlas, return NULL; } } - // be sure to set texture for fast lookup - newPlot->fTexture = fTexture; - if (!newPlot->addSubImage(width, height, image, loc)) { - this->freePlot(newPlot); - return NULL; + // now look through all allocated plots for one we can share, in MRU order + GrPlotList::Iter plotIter; + plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart); + GrPlot* plot; + while (NULL != (plot = plotIter.get())) { + // make sure texture is set for quick lookup + plot->fTexture = fTexture; + if (plot->addSubImage(width, height, image, loc)) { + this->moveToHead(plot); + // new plot for atlas, put at end of array + *(atlas->fPlots.append()) = plot; + return plot; + } + plotIter.next(); } - // new plot, put at head - newPlot->fNext = atlas->fPlots; - atlas->fPlots = newPlot; - - return newPlot; + // If the above fails, then the current plot list has no room + return NULL; } -bool GrAtlasMgr::removeUnusedPlots(GrAtlas* atlas) { - - // GrPlot** is used so that the head element can be easily - // modified when the first element is deleted - GrPlot** plotRef = &atlas->fPlots; - GrPlot* plot = atlas->fPlots; - bool removed = false; - while (NULL != plot) { - if (plot->drawToken().isIssued()) { - *plotRef = plot->fNext; - this->freePlot(plot); - plot = *plotRef; - removed = true; - } else { - plotRef = &plot->fNext; - plot = plot->fNext; +bool GrAtlasMgr::removePlot(GrAtlas* atlas, const GrPlot* plot) { + // iterate through plot list for this atlas + int count = atlas->fPlots.count(); + for (int i = 0; i < count; ++i) { + if (plot == atlas->fPlots[i]) { + atlas->fPlots.remove(i); + return true; } } - return removed; + return false; } -void GrAtlasMgr::deletePlotList(GrPlot* plot) { - while (NULL != plot) { - GrPlot* next = plot->fNext; - this->freePlot(plot); - plot = next; - } -} - -GrPlot* GrAtlasMgr::allocPlot() { - if (NULL == fFreePlots) { - return NULL; - } else { - GrPlot* alloc = fFreePlots; - fFreePlots = alloc->fNext; -#ifdef SK_DEBUG -// GrPrintf(" GrPlot %p [%d %d] %d\n", this, alloc->fOffset.fX, alloc->fOffset.fY, gCounter); - gCounter += 1; -#endif - return alloc; +// get a plot that's not being used by the current draw +GrPlot* GrAtlasMgr::getUnusedPlot() { + GrPlotList::Iter plotIter; + plotIter.init(fPlotList, GrPlotList::Iter::kTail_IterStart); + GrPlot* plot; + while (NULL != (plot = plotIter.get())) { + if (plot->drawToken().isIssued()) { + return plot; + } + plotIter.prev(); } + return NULL; } -void GrAtlasMgr::freePlot(GrPlot* plot) { - SkASSERT(this == plot->fAtlasMgr); - - plot->fRects->reset(); - plot->fNext = fFreePlots; - fFreePlots = plot; - -#ifdef SK_DEBUG - --gCounter; -// GrPrintf("~GrPlot %p [%d %d] %d\n", this, plot->fOffset.fX, plot->fOffset.fY, gCounter); -#endif +void GrAtlasMgr::uploadPlotsToTexture() { + if (fBatchUploads) { + GrPlotList::Iter plotIter; + plotIter.init(fPlotList, GrPlotList::Iter::kHead_IterStart); + GrPlot* plot; + while (NULL != (plot = plotIter.get())) { + plot->uploadToTexture(); + plotIter.next(); + } + } } |