summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/skia/src/gpu/GrAtlas.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/skia/src/gpu/GrAtlas.cpp')
-rw-r--r--chromium/third_party/skia/src/gpu/GrAtlas.cpp346
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();
+ }
+ }
}