Index: src/gpu/gl/GrGpuGL_program.cpp |
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp |
index 833d811588555dc354e2a7ae903ec6d2dea87e71..b017d5de40b68580b097a844dfd8224ec16bbb7f 100644 |
--- a/src/gpu/gl/GrGpuGL_program.cpp |
+++ b/src/gpu/gl/GrGpuGL_program.cpp |
@@ -9,12 +9,32 @@ |
#include "GrEffect.h" |
#include "GrGLEffect.h" |
+#include "SkTSearch.h" |
typedef GrGLUniformManager::UniformHandle UniformHandle; |
static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle; |
-#define SKIP_CACHE_CHECK true |
-#define GR_UINT32_MAX static_cast<uint32_t>(-1) |
+struct GrGpuGL::ProgramCache::Entry { |
+ SK_DECLARE_INST_COUNT_ROOT(Entry); |
+ Entry() : fProgram(NULL), fLRUStamp(0) {} |
+ |
+ SkAutoTUnref<GrGLProgram> fProgram; |
+ unsigned int fLRUStamp; |
+}; |
+ |
+SK_DEFINE_INST_COUNT(GrGpuGL::ProgramCache::Entry); |
+ |
+struct GrGpuGL::ProgramCache::ProgDescLess { |
+ bool operator() (const GrGLProgramDesc& desc, const Entry* entry) { |
+ GrAssert(NULL != entry->fProgram.get()); |
+ return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc()); |
+ } |
+ |
+ bool operator() (const Entry* entry, const GrGLProgramDesc& desc) { |
+ GrAssert(NULL != entry->fProgram.get()); |
+ return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc); |
+ } |
+}; |
GrGpuGL::ProgramCache::ProgramCache(const GrGLContext& gl) |
: fCount(0) |
@@ -23,70 +43,147 @@ GrGpuGL::ProgramCache::ProgramCache(const GrGLContext& gl) |
#ifdef PROGRAM_CACHE_STATS |
, fTotalRequests(0) |
, fCacheMisses(0) |
+ , fHashMisses(0) |
#endif |
{ |
+ for (int i = 0; i < 1 << kHashBits; ++i) { |
+ fHashTable[i] = NULL; |
+ } |
} |
GrGpuGL::ProgramCache::~ProgramCache() { |
+ for (int i = 0; i < fCount; ++i){ |
+ SkDELETE(fEntries[i]); |
+ } |
// dump stats |
#ifdef PROGRAM_CACHE_STATS |
SkDebugf("--- Program Cache ---\n"); |
SkDebugf("Total requests: %d\n", fTotalRequests); |
SkDebugf("Cache misses: %d\n", fCacheMisses); |
- SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) |
- ? (float)fCacheMisses/(float)fTotalRequests : 0.0f); |
+ SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? |
+ 100.f * fCacheMisses / fTotalRequests : |
+ 0.f); |
+ int cacheHits = fTotalRequests - fCacheMisses; |
+ SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); |
SkDebugf("---------------------\n"); |
#endif |
} |
void GrGpuGL::ProgramCache::abandon() { |
for (int i = 0; i < fCount; ++i) { |
- GrAssert(NULL != fEntries[i].fProgram.get()); |
- fEntries[i].fProgram->abandon(); |
- fEntries[i].fProgram.reset(NULL); |
+ GrAssert(NULL != fEntries[i]->fProgram.get()); |
+ fEntries[i]->fProgram->abandon(); |
+ fEntries[i]->fProgram.reset(NULL); |
} |
fCount = 0; |
} |
+int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const { |
+ ProgDescLess less; |
+ return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); |
+} |
+ |
GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc, |
const GrEffectStage* stages[]) { |
- Entry newEntry; |
- newEntry.fKey.setKeyData(desc.asKey()); |
#ifdef PROGRAM_CACHE_STATS |
++fTotalRequests; |
#endif |
- Entry* entry = fHashCache.find(newEntry.fKey); |
+ Entry* entry = NULL; |
+ |
+ uint32_t hashIdx = desc.getChecksum(); |
+ hashIdx ^= hashIdx >> 16; |
+ if (kHashBits <= 8) { |
+ hashIdx ^= hashIdx >> 8; |
+ } |
+ hashIdx &=((1 << kHashBits) - 1); |
+ Entry* hashedEntry = fHashTable[hashIdx]; |
+ if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) { |
+ GrAssert(NULL != hashedEntry->fProgram); |
+ entry = hashedEntry; |
+ } |
+ |
+ int entryIdx; |
+ if (NULL == entry) { |
+ entryIdx = this->search(desc); |
+ if (entryIdx >= 0) { |
+ entry = fEntries[entryIdx]; |
+#ifdef PROGRAM_CACHE_STATS |
+ ++fHashMisses; |
+#endif |
+ } |
+ } |
+ |
if (NULL == entry) { |
+ // We have a cache miss |
#ifdef PROGRAM_CACHE_STATS |
++fCacheMisses; |
#endif |
- newEntry.fProgram.reset(GrGLProgram::Create(fGL, desc, stages)); |
- if (NULL == newEntry.fProgram.get()) { |
+ GrGLProgram* program = GrGLProgram::Create(fGL, desc, stages); |
+ if (NULL == program) { |
return NULL; |
} |
+ int purgeIdx = 0; |
if (fCount < kMaxEntries) { |
- entry = fEntries + fCount; |
- ++fCount; |
+ entry = SkNEW(Entry); |
+ purgeIdx = fCount++; |
+ fEntries[purgeIdx] = entry; |
} else { |
- GrAssert(kMaxEntries == fCount); |
- entry = fEntries; |
+ GrAssert(fCount == kMaxEntries); |
+ purgeIdx = 0; |
for (int i = 1; i < kMaxEntries; ++i) { |
- if (fEntries[i].fLRUStamp < entry->fLRUStamp) { |
- entry = fEntries + i; |
+ if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { |
+ purgeIdx = i; |
} |
} |
- fHashCache.remove(entry->fKey, entry); |
+ entry = fEntries[purgeIdx]; |
+ int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1); |
+ if (fHashTable[purgedHashIdx] == entry) { |
+ fHashTable[purgedHashIdx] = NULL; |
+ } |
} |
- *entry = newEntry; |
- fHashCache.insert(entry->fKey, entry); |
+ GrAssert(fEntries[purgeIdx] == entry); |
+ entry->fProgram.reset(program); |
+ // We need to shift fEntries around so that the entry currently at purgeIdx is placed |
+ // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor). |
+ entryIdx = ~entryIdx; |
+ if (entryIdx < purgeIdx) { |
+ // Let E and P be the entries at index entryIdx and purgeIdx, respectively. |
+ // If the entries array looks like this: |
+ // aaaaEbbbbbPccccc |
+ // we rearrange it to look like this: |
+ // aaaaPEbbbbbccccc |
+ size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); |
+ memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); |
+ fEntries[entryIdx] = entry; |
+ } else if (purgeIdx < entryIdx) { |
+ // If the entries array looks like this: |
+ // aaaaPbbbbbEccccc |
+ // we rearrange it to look like this: |
+ // aaaabbbbbPEccccc |
+ size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); |
+ memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); |
+ fEntries[entryIdx - 1] = entry; |
+ } |
+#if GR_DEBUG |
+ GrAssert(NULL != fEntries[0]->fProgram.get()); |
+ for (int i = 0; i < fCount - 1; ++i) { |
+ GrAssert(NULL != fEntries[i + 1]->fProgram.get()); |
+ const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc(); |
+ const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc(); |
+ GrAssert(GrGLProgramDesc::Less(a, b)); |
+ GrAssert(!GrGLProgramDesc::Less(b, a)); |
+ } |
+#endif |
} |
+ fHashTable[hashIdx] = entry; |
entry->fLRUStamp = fCurrLRUStamp; |
- if (GR_UINT32_MAX == fCurrLRUStamp) { |
+ |
+ if (SK_MaxU32 == fCurrLRUStamp) { |
// wrap around! just trash our LRU, one time hit. |
for (int i = 0; i < fCount; ++i) { |
- fEntries[i].fLRUStamp = 0; |
+ fEntries[i]->fLRUStamp = 0; |
} |
} |
++fCurrLRUStamp; |
@@ -177,9 +274,6 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC |
} |
const GrEffectStage* stages[GrDrawState::kNumStages]; |
- for (int i = 0; i < GrDrawState::kNumStages; ++i) { |
- stages[i] = drawState.isStageEnabled(i) ? &drawState.getStage(i) : NULL; |
- } |
GrGLProgramDesc desc; |
GrGLProgramDesc::Build(this->getDrawState(), |
kDrawPoints_DrawType == type, |
@@ -188,6 +282,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC |
dstCoeff, |
this, |
dstCopy, |
+ stages, |
&desc); |
fCurrentProgram.reset(fProgramCache->getProgram(desc, stages)); |
@@ -206,19 +301,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC |
fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff); |
this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); |
- GrColor color; |
- GrColor coverage; |
- if (blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag) { |
- color = 0; |
- coverage = 0; |
- } else if (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) { |
- color = 0xffffffff; |
- coverage = drawState.getCoverage(); |
- } else { |
- color = drawState.getColor(); |
- coverage = drawState.getCoverage(); |
- } |
- fCurrentProgram->setData(this, color, coverage, dstCopy, &fSharedGLProgramState); |
+ fCurrentProgram->setData(this, blendOpts, stages, dstCopy, &fSharedGLProgramState); |
} |
this->flushStencil(type); |
this->flushScissor(); |