aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-14 15:14:51 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-14 15:14:51 +0000
commit7801faaab9bf7dd0ac67e859c4e284e74f7bd46f (patch)
treee2bdd76e9b8ed3f5837f449d106cf1b6f153f6ef /src
parent21bbc2816f197d38baf8e82983aa63b439596a11 (diff)
Add CPU backing store for GrAtlas to reduce texture uploads.
This change creates a temporary copy of each GrPlot on the CPU side. As we add glyphs to a plot, a dirty rectangle is tracked, and just before we render text we upload all of the dirty areas to the atlas texture. Once a plot is nearly full, we assume that we may only be adding one or two glyphs before rendering, so we delete the CPU backing memory to save space, and upload directly. BUG=366225 R=robertphillips@google.com Author: jvanverth@google.com Review URL: https://codereview.chromium.org/269423007 git-svn-id: http://skia.googlecode.com/svn/trunk@14729 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrAtlas.cpp118
-rw-r--r--src/gpu/GrAtlas.h14
-rwxr-xr-xsrc/gpu/GrBitmapTextContext.cpp2
-rw-r--r--src/gpu/GrBitmapTextContext.h1
-rwxr-xr-xsrc/gpu/GrDistanceFieldTextContext.cpp2
-rw-r--r--src/gpu/GrLayerCache.cpp2
-rw-r--r--src/gpu/GrTextStrike.cpp4
-rw-r--r--src/gpu/GrTextStrike.h8
8 files changed, 129 insertions, 22 deletions
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp
index 008bae2f00..c4e1a3b618 100644
--- a/src/gpu/GrAtlas.cpp
+++ b/src/gpu/GrAtlas.cpp
@@ -24,19 +24,28 @@ GrPlot::GrPlot() : fDrawToken(NULL, 0)
, fRects(NULL)
, fAtlasMgr(NULL)
, fBytesPerPixel(1)
+ , fDirty(false)
+ , fBatchUploads(false)
{
fOffset.set(0, 0);
}
GrPlot::~GrPlot() {
+ SkDELETE_ARRAY(fPlotData);
+ fPlotData = NULL;
delete fRects;
}
-void GrPlot::init(GrAtlasMgr* mgr, int offX, int offY, int width, int height, size_t bpp) {
+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 void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset) {
@@ -46,20 +55,46 @@ static inline void adjust_for_offset(GrIPoint16* loc, const GrIPoint16& offset)
bool GrPlot::addSubImage(int width, int height, const void* image,
GrIPoint16* loc) {
+ float percentFull = fRects->percentFull();
if (!fRects->addRect(width, height, loc)) {
return false;
}
- SkAutoSMalloc<1024> storage;
- 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, width, height,
- fTexture->config(), image, 0,
- GrContext::kDontFlush_PixelOpsFlag);
+ // 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;
+ }
+
+ 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();
+ context->writeTexturePixels(fTexture,
+ loc->fX, loc->fY, width, height,
+ fTexture->config(), image, 0,
+ GrContext::kDontFlush_PixelOpsFlag);
+ }
#if FONT_CACHE_STATS
++g_UploadCount;
@@ -68,6 +103,39 @@ 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) {
+ 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.
+ int 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();
@@ -77,19 +145,23 @@ void GrPlot::resetRects() {
GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config,
const SkISize& backingTextureSize,
- int numPlotsX, int numPlotsY) {
+ int numPlotsX, int numPlotsY, bool batchUploads) {
fGpu = SkRef(gpu);
fPixelConfig = config;
fBackingTextureSize = backingTextureSize;
fNumPlotsX = numPlotsX;
fNumPlotsY = numPlotsY;
+ fBatchUploads = batchUploads;
fTexture = NULL;
- int plotWidth = fBackingTextureSize.width() / fNumPlotsX;
- int plotHeight = fBackingTextureSize.height() / fNumPlotsY;
+ int textureWidth = fBackingTextureSize.width();
+ int textureHeight = fBackingTextureSize.height();
- SkASSERT(plotWidth * fNumPlotsX == fBackingTextureSize.width());
- SkASSERT(plotHeight * fNumPlotsY == fBackingTextureSize.height());
+ int plotWidth = textureWidth / fNumPlotsX;
+ int plotHeight = textureHeight / fNumPlotsY;
+
+ SkASSERT(plotWidth * fNumPlotsX == textureWidth);
+ SkASSERT(plotHeight * fNumPlotsY == textureHeight);
// set up allocated plots
size_t bpp = GrBytesPerPixel(fPixelConfig);
@@ -98,7 +170,7 @@ GrAtlasMgr::GrAtlasMgr(GrGpu* gpu, GrPixelConfig config,
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);
+ currPlot->init(this, x, y, plotWidth, plotHeight, bpp, batchUploads);
// build LRU list
fPlotList.addToHead(currPlot);
@@ -201,3 +273,15 @@ GrPlot* GrAtlasMgr::getUnusedPlot() {
return NULL;
}
+
+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();
+ }
+ }
+}
diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h
index d7f5334141..b3affe20a4 100644
--- a/src/gpu/GrAtlas.h
+++ b/src/gpu/GrAtlas.h
@@ -40,21 +40,28 @@ public:
GrDrawTarget::DrawToken drawToken() const { return fDrawToken; }
void setDrawToken(GrDrawTarget::DrawToken draw) { fDrawToken = draw; }
+ void uploadToTexture();
+
void resetRects();
private:
GrPlot();
~GrPlot(); // does not try to delete the fNext field
- void init(GrAtlasMgr* mgr, int offX, int offY, int width, int height, size_t bpp);
+ void init(GrAtlasMgr* mgr, int offX, int offY, int width, int height, size_t bpp,
+ bool batchUploads);
// for recycling
GrDrawTarget::DrawToken fDrawToken;
+ unsigned char* fPlotData;
GrTexture* fTexture;
GrRectanizer* fRects;
GrAtlasMgr* fAtlasMgr;
GrIPoint16 fOffset; // the offset of the plot in the backing texture
size_t fBytesPerPixel;
+ SkIRect fDirtyRect;
+ bool fDirty;
+ bool fBatchUploads;
friend class GrAtlasMgr;
};
@@ -64,7 +71,7 @@ typedef SkTInternalLList<GrPlot> GrPlotList;
class GrAtlasMgr {
public:
GrAtlasMgr(GrGpu*, GrPixelConfig, const SkISize& backingTextureSize,
- int numPlotsX, int numPlotsY);
+ int numPlotsX, int numPlotsY, bool batchUploads);
~GrAtlasMgr();
// add subimage of width, height dimensions to atlas
@@ -82,6 +89,8 @@ public:
return fTexture;
}
+ void uploadPlotsToTexture();
+
private:
void moveToHead(GrPlot* plot);
@@ -91,6 +100,7 @@ private:
SkISize fBackingTextureSize;
int fNumPlotsX;
int fNumPlotsY;
+ bool fBatchUploads;
// allocated array of GrPlots
GrPlot* fPlotArray;
diff --git a/src/gpu/GrBitmapTextContext.cpp b/src/gpu/GrBitmapTextContext.cpp
index 3b5e27a90c..744d97f426 100755
--- a/src/gpu/GrBitmapTextContext.cpp
+++ b/src/gpu/GrBitmapTextContext.cpp
@@ -68,6 +68,8 @@ void GrBitmapTextContext::flushGlyphs() {
drawState->setFromPaint(fPaint, SkMatrix::I(), fContext->getRenderTarget());
if (fCurrVertex > 0) {
+ fContext->getFontCache()->updateTextures();
+
// setup our sampler state for our text texture/atlas
SkASSERT(SkIsAlign4(fCurrVertex));
SkASSERT(fCurrTexture);
diff --git a/src/gpu/GrBitmapTextContext.h b/src/gpu/GrBitmapTextContext.h
index 18f5cc0eef..23248ebf65 100644
--- a/src/gpu/GrBitmapTextContext.h
+++ b/src/gpu/GrBitmapTextContext.h
@@ -11,6 +11,7 @@
#include "GrTextContext.h"
class GrTextStrike;
+class GrAtlasMgr;
/*
* This class implements GrTextContext using standard bitmap fonts
diff --git a/src/gpu/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp
index 512420eb67..afc2a47ff1 100755
--- a/src/gpu/GrDistanceFieldTextContext.cpp
+++ b/src/gpu/GrDistanceFieldTextContext.cpp
@@ -101,6 +101,8 @@ void GrDistanceFieldTextContext::flushGlyphs() {
drawState->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget());
if (fCurrVertex > 0) {
+ fContext->getFontCache()->updateTextures();
+
// setup our sampler state for our text texture/atlas
SkASSERT(SkIsAlign4(fCurrVertex));
SkASSERT(fCurrTexture);
diff --git a/src/gpu/GrLayerCache.cpp b/src/gpu/GrLayerCache.cpp
index d1346c5e50..0621aace44 100644
--- a/src/gpu/GrLayerCache.cpp
+++ b/src/gpu/GrLayerCache.cpp
@@ -58,7 +58,7 @@ void GrLayerCache::init() {
// The layer cache only gets 1 plot
SkISize textureSize = SkISize::Make(kAtlasTextureWidth, kAtlasTextureHeight);
fAtlasMgr.reset(SkNEW_ARGS(GrAtlasMgr, (fGpu, kSkia8888_GrPixelConfig,
- textureSize, 1, 1)));
+ textureSize, 1, 1, false)));
}
void GrLayerCache::freeAll() {
diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp
index 57263d4e7d..b03d14f96f 100644
--- a/src/gpu/GrTextStrike.cpp
+++ b/src/gpu/GrTextStrike.cpp
@@ -5,7 +5,6 @@
* found in the LICENSE file.
*/
-#include "GrAtlas.h"
#include "GrGpu.h"
#include "GrRectanizer.h"
#include "GrTextStrike.h"
@@ -86,7 +85,8 @@ GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
fAtlasMgr[atlasIndex] = SkNEW_ARGS(GrAtlasMgr, (fGpu, config,
textureSize,
GR_NUM_PLOTS_X,
- GR_NUM_PLOTS_Y));
+ GR_NUM_PLOTS_Y,
+ true));
}
GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
(this, scaler->getKey(), format, fAtlasMgr[atlasIndex]));
diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h
index 0355405b3c..f71b82c88b 100644
--- a/src/gpu/GrTextStrike.h
+++ b/src/gpu/GrTextStrike.h
@@ -90,6 +90,14 @@ public:
}
GrTextStrike* getHeadStrike() const { return fHead; }
+ void updateTextures() {
+ for (int i = 0; i < kAtlasCount; ++i) {
+ if (fAtlasMgr[i]) {
+ fAtlasMgr[i]->uploadPlotsToTexture();
+ }
+ }
+ }
+
#ifdef SK_DEBUG
void validate() const;
#else