diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp index 0bdbe54d54..be5b1b01d1 100644 --- a/src/effects/gradients/SkGradientShader.cpp +++ b/src/effects/gradients/SkGradientShader.cpp @@ -672,20 +672,38 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END #if SK_SUPPORT_GPU +#include "effects/GrTextureStripAtlas.h" #include "SkGr.h" GrGLGradientStage::GrGLGradientStage(const GrProgramStageFactory& factory) - : INHERITED(factory) { } + : INHERITED(factory) + , fCachedYCoord(GR_ScalarMax) + , fFSYUni(GrGLUniformManager::kInvalidUniformHandle) { } GrGLGradientStage::~GrGLGradientStage() { } +void GrGLGradientStage::setupVariables(GrGLShaderBuilder* builder) { + fFSYUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, + kFloat_GrSLType, "GradientYCoordFS"); +} + +void GrGLGradientStage::setData(const GrGLUniformManager& uman, + const GrCustomStage& stage, + const GrRenderTarget*, + int stageNum) { + GrScalar yCoord = static_cast(stage).getYCoord(); + if (yCoord != fCachedYCoord) { + uman.set1f(fFSYUni, yCoord); + fCachedYCoord = yCoord; + } +} + void GrGLGradientStage::emitColorLookup(GrGLShaderBuilder* builder, const char* tName, const char* outputColor, const char* samplerName) { - // Texture is effectively 1D so the y coordinate is 0.5, if we pack multiple - // gradients into a texture, we could instead pick the appropriate row here - builder->fSampleCoords.printf("vec2(%s, 0.5)", tName); + builder->fSampleCoords.printf("vec2(%s, %s)", tName, + builder->getUniformVariable(fFSYUni).c_str()); builder->fComplexCoord = true; builder->emitDefaultFetch(outputColor, samplerName); } @@ -696,7 +714,7 @@ GrGradientEffect::GrGradientEffect(GrContext* ctx, const SkGradientShaderBase& shader, GrSamplerState* sampler) : fTexture (NULL) - , fUseTexture (false) { + , fUseTexture (true) { // TODO: check for simple cases where we don't need a texture: //GradientInfo info; //shader.asAGradient(&info); @@ -705,19 +723,38 @@ GrGradientEffect::GrGradientEffect(GrContext* ctx, SkBitmap bitmap; shader.getGradientTableBitmap(&bitmap); - fTexture = GrLockCachedBitmapTexture(ctx, bitmap, - sampler->textureParams()); - SkSafeRef(fTexture); - fUseTexture = true; - - // Unlock immediately, this is not great, but we don't have a way of - // knowing when else to unlock it currently, so it may get purged from - // the cache, but it'll still be ref'd until it's no longer being used. - GrUnlockCachedBitmapTexture(fTexture); + GrTextureStripAtlas::Desc desc; + desc.fWidth = bitmap.width(); + desc.fHeight = 32; + desc.fRowHeight = bitmap.height(); + desc.fContext = ctx; + desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config()); + fAtlas = GrTextureStripAtlas::GetAtlas(desc); + GrAssert(NULL != fAtlas); + + fRow = fAtlas->lockRow(bitmap); + if (-1 != fRow) { + fYCoord = fAtlas->getYOffset(fRow) + GR_ScalarHalf * + fAtlas->getVerticalScaleFactor(); + fTexture = fAtlas->getTexture(); + } else { + fTexture = GrLockCachedBitmapTexture(ctx, bitmap, sampler->textureParams()); + SkSafeRef(fTexture); + fYCoord = GR_ScalarHalf; + + // Unlock immediately, this is not great, but we don't have a way of + // knowing when else to unlock it currently, so it may get purged from + // the cache, but it'll still be ref'd until it's no longer being used. + GrUnlockCachedBitmapTexture(fTexture); + } } GrGradientEffect::~GrGradientEffect() { - SkSafeUnref(fTexture); + if (this->useAtlas()) { + fAtlas->unlockRow(fRow); + } else { + SkSafeUnref(fTexture); + } } unsigned int GrGradientEffect::numTextures() const { diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h index 8152d5fb71..4c3b6c777f 100644 --- a/src/effects/gradients/SkGradientShaderPriv.h +++ b/src/effects/gradients/SkGradientShaderPriv.h @@ -220,6 +220,8 @@ class GrProgramStageFactory; * determines the gradient value. */ + class GrTextureStripAtlas; + // Base class for Gr gradient effects class GrGradientEffect : public GrCustomStage { public: @@ -233,6 +235,14 @@ public: GrTexture* texture(unsigned int index) const; bool useTexture() const { return fUseTexture; } + bool useAtlas() const { return SkToBool(-1 != fRow); } + GrScalar getYCoord() const { GrAssert(fUseTexture); return fYCoord; }; + + bool isEqual(const GrCustomStage& stage) const { + const GrGradientEffect& s = static_cast(stage); + return INHERITED::isEqual(stage) && this->useAtlas() == s.useAtlas() && + fYCoord == s.getYCoord(); + } protected: @@ -252,6 +262,9 @@ protected: private: GrTexture* fTexture; bool fUseTexture; + GrScalar fYCoord; + GrTextureStripAtlas* fAtlas; + int fRow; typedef GrCustomStage INHERITED; @@ -266,6 +279,12 @@ public: GrGLGradientStage(const GrProgramStageFactory& factory); virtual ~GrGLGradientStage(); + virtual void setupVariables(GrGLShaderBuilder* builder) SK_OVERRIDE; + virtual void setData(const GrGLUniformManager&, + const GrCustomStage&, + const GrRenderTarget*, + int stageNum) SK_OVERRIDE; + // emit code that gets a fragment's color from an expression for t; for now // this always uses the texture, but for simpler cases we'll be able to lerp void emitColorLookup(GrGLShaderBuilder* builder, const char* t, @@ -273,6 +292,9 @@ public: private: + GrScalar fCachedYCoord; + GrGLUniformManager::UniformHandle fFSYUni; + typedef GrGLProgramStage INHERITED; }; diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp index 528291bdc1..e1d8cb4d1e 100644 --- a/src/effects/gradients/SkTwoPointConicalGradient.cpp +++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp @@ -468,6 +468,7 @@ GrGLConical2Gradient::GrGLConical2Gradient( } void GrGLConical2Gradient::setupVariables(GrGLShaderBuilder* builder) { + INHERITED::setupVariables(builder); // 2 copies of uniform array, 1 for each of vertex & fragment shader, // to work around Xoom bug. Doesn't seem to cause performance decrease // in test apps, but need to keep an eye on it. @@ -631,8 +632,9 @@ void GrGLConical2Gradient::emitFS(GrGLShaderBuilder* builder, void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, const GrCustomStage& baseData, - const GrRenderTarget*, + const GrRenderTarget* target, int stageNum) { + INHERITED::setData(uman, baseData, target, stageNum); const GrConical2Gradient& data = static_cast(baseData); GrAssert(data.isDegenerate() == fIsDegenerate); diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp index 441f5a8d09..9d937713fd 100644 --- a/src/effects/gradients/SkTwoPointRadialGradient.cpp +++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp @@ -501,6 +501,7 @@ GrGLRadial2Gradient::GrGLRadial2Gradient( } void GrGLRadial2Gradient::setupVariables(GrGLShaderBuilder* builder) { + INHERITED::setupVariables(builder); // 2 copies of uniform array, 1 for each of vertex & fragment shader, // to work around Xoom bug. Doesn't seem to cause performance decrease // in test apps, but need to keep an eye on it. @@ -606,8 +607,9 @@ void GrGLRadial2Gradient::emitFS(GrGLShaderBuilder* builder, void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, const GrCustomStage& baseData, - const GrRenderTarget*, + const GrRenderTarget* target, int stageNum) { + INHERITED::setData(uman, baseData, target, stageNum); const GrRadial2Gradient& data = static_cast(baseData); GrAssert(data.isDegenerate() == fIsDegenerate); diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp index a1a9d0301c..f31d7d72a6 100644 --- a/src/gpu/effects/GrTextureStripAtlas.cpp +++ b/src/gpu/effects/GrTextureStripAtlas.cpp @@ -34,14 +34,8 @@ public: GrTextureStripAtlas* fAtlas; }; -// Ugly way of ensuring that we clean up the atlases on exit -struct AtlasEntries { - ~AtlasEntries() { fEntries.deleteAll(); } - SkTDArray fEntries; -}; - GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) { - static AtlasEntries gAtlasEntries; + static SkTDArray gAtlasEntries; static GrTHashTable gAtlasCache; AtlasHashKey key; key.setKeyData(desc.asKey()); @@ -49,8 +43,7 @@ GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::De if (NULL != entry) { return entry->fAtlas; } else { - entry = SkNEW(AtlasEntry); - gAtlasEntries.fEntries.push(entry); + entry = gAtlasEntries.push(); entry->fAtlas = SkNEW_ARGS(GrTextureStripAtlas, (desc)); entry->fKey = key; gAtlasCache.insert(key, entry); @@ -93,6 +86,7 @@ int GrTextureStripAtlas::lockRow(const SkBitmap& data) { this->removeFromLRU(row); } ++row->fLocks; + ++fLockedRows; // Since all the rows are always stored in a contiguous array, we can save the memory // required for storing row numbers and just compute it with some pointer arithmetic @@ -104,11 +98,14 @@ int GrTextureStripAtlas::lockRow(const SkBitmap& data) { // We don't have this data cached, so pick the least recently used row to copy into AtlasRow* row = this->getLRU(); + ++fLockedRows; + if (NULL == row) { // force a flush, which should unlock all the rows; then try again fDesc.fContext->flush(); row = this->getLRU(); if (NULL == row) { + --fLockedRows; return -1; } } @@ -116,8 +113,6 @@ int GrTextureStripAtlas::lockRow(const SkBitmap& data) { this->removeFromLRU(row); uint32_t oldKey = row->fKey; - row->fKey = key; - row->fLocks = 1; // If we are writing into a row that already held bitmap data, we need to remove the // reference to that genID which is stored in our sorted table of key values. @@ -126,13 +121,15 @@ int GrTextureStripAtlas::lockRow(const SkBitmap& data) { // Find the entry in the list; if it's before the index where we plan on adding the new // entry, we decrement since it will shift elements ahead of it back by one. int oldIndex = this->searchByKey(oldKey); - if (oldIndex <= index) { + if (oldIndex < index) { --index; } fKeyTable.remove(oldIndex); } + row->fKey = key; + row->fLocks = 1; fKeyTable.insert(index, 1, &row); rowNumber = static_cast(row - fRows); @@ -149,7 +146,6 @@ int GrTextureStripAtlas::lockRow(const SkBitmap& data) { GrContext::kDontFlush_PixelOpsFlag); } - ++fLockedRows; GrAssert(rowNumber >= 0); VALIDATE; return rowNumber; @@ -233,12 +229,16 @@ void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) { if (NULL == row->fNext) { GrAssert(row == fLRUBack); fLRUBack = row->fPrev; - fLRUBack->fNext = NULL; + if (fLRUBack) { + fLRUBack->fNext = NULL; + } } if (NULL == row->fPrev) { GrAssert(row == fLRUFront); fLRUFront = row->fNext; - fLRUFront->fPrev = NULL; + if (fLRUFront) { + fLRUFront->fPrev = NULL; + } } } row->fNext = NULL; @@ -303,8 +303,10 @@ void GrTextureStripAtlas::validate() { GrAssert(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0); } - // Our count of locks should equal the sum of row locks - GrAssert(rowLocks == fLockedRows); + // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed, + // in which case we'll have one more lock than recorded in the rows (to represent the pending + // lock of a row; which ensures we don't unlock the texture prematurely). + GrAssert(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows); // We should have one lru entry for each free row GrAssert(freeRows == lruCount);