| /* | |
| * Copyright 2011 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "GrGLGpu.h" | |
| #include "builders/GrGLProgramBuilder.h" | |
| #include "GrProcessor.h" | |
| #include "GrGLPathRendering.h" | |
| #include "glsl/GrGLSLFragmentProcessor.h" | |
| #include "glsl/GrGLSLProgramDataManager.h" | |
| #include "SkRTConf.h" | |
| #include "SkTSearch.h" | |
| #ifdef PROGRAM_CACHE_STATS | |
| SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, | |
| "Display program cache usage."); | |
| #endif | |
| typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; | |
| struct GrGLGpu::ProgramCache::Entry { | |
| Entry() : fProgram(nullptr), fLRUStamp(0) {} | |
| SkAutoTUnref<GrGLProgram> fProgram; | |
| unsigned int fLRUStamp; | |
| }; | |
| struct GrGLGpu::ProgramCache::ProgDescLess { | |
| bool operator() (const GrProgramDesc& desc, const Entry* entry) { | |
| SkASSERT(entry->fProgram.get()); | |
| return GrProgramDesc::Less(desc, entry->fProgram->getDesc()); | |
| } | |
| bool operator() (const Entry* entry, const GrProgramDesc& desc) { | |
| SkASSERT(entry->fProgram.get()); | |
| return GrProgramDesc::Less(entry->fProgram->getDesc(), desc); | |
| } | |
| }; | |
| GrGLGpu::ProgramCache::ProgramCache(GrGLGpu* gpu) | |
| : fCount(0) | |
| , fCurrLRUStamp(0) | |
| , fGpu(gpu) | |
| #ifdef PROGRAM_CACHE_STATS | |
| , fTotalRequests(0) | |
| , fCacheMisses(0) | |
| , fHashMisses(0) | |
| #endif | |
| { | |
| for (int i = 0; i < 1 << kHashBits; ++i) { | |
| fHashTable[i] = nullptr; | |
| } | |
| } | |
| GrGLGpu::ProgramCache::~ProgramCache() { | |
| for (int i = 0; i < fCount; ++i){ | |
| delete fEntries[i]; | |
| } | |
| // dump stats | |
| #ifdef PROGRAM_CACHE_STATS | |
| if (c_DisplayCache) { | |
| SkDebugf("--- Program Cache ---\n"); | |
| SkDebugf("Total requests: %d\n", fTotalRequests); | |
| SkDebugf("Cache misses: %d\n", fCacheMisses); | |
| 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 GrGLGpu::ProgramCache::reset() { | |
| for (int i = 0; i < fCount; ++i) { | |
| SkASSERT(fEntries[i]->fProgram.get()); | |
| fEntries[i]->fProgram->abandon(); | |
| delete fEntries[i]; | |
| fEntries[i] = nullptr; | |
| } | |
| fCount = 0; | |
| // zero out hash table | |
| for (int i = 0; i < 1 << kHashBits; i++) { | |
| fHashTable[i] = nullptr; | |
| } | |
| fCurrLRUStamp = 0; | |
| #ifdef PROGRAM_CACHE_STATS | |
| fTotalRequests = 0; | |
| fCacheMisses = 0; | |
| fHashMisses = 0; | |
| #endif | |
| } | |
| void GrGLGpu::ProgramCache::abandon() { | |
| this->reset(); | |
| } | |
| int GrGLGpu::ProgramCache::search(const GrProgramDesc& desc) const { | |
| ProgDescLess less; | |
| return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); | |
| } | |
| GrGLProgram* GrGLGpu::ProgramCache::refProgram(const DrawArgs& args) { | |
| #ifdef PROGRAM_CACHE_STATS | |
| ++fTotalRequests; | |
| #endif | |
| Entry* entry = nullptr; | |
| uint32_t hashIdx = args.fDesc->getChecksum(); | |
| hashIdx ^= hashIdx >> 16; | |
| if (kHashBits <= 8) { | |
| hashIdx ^= hashIdx >> 8; | |
| } | |
| hashIdx &=((1 << kHashBits) - 1); | |
| Entry* hashedEntry = fHashTable[hashIdx]; | |
| if (hashedEntry && hashedEntry->fProgram->getDesc() == *args.fDesc) { | |
| SkASSERT(hashedEntry->fProgram); | |
| entry = hashedEntry; | |
| } | |
| int entryIdx; | |
| if (nullptr == entry) { | |
| entryIdx = this->search(*args.fDesc); | |
| if (entryIdx >= 0) { | |
| entry = fEntries[entryIdx]; | |
| #ifdef PROGRAM_CACHE_STATS | |
| ++fHashMisses; | |
| #endif | |
| } | |
| } | |
| if (nullptr == entry) { | |
| // We have a cache miss | |
| #ifdef PROGRAM_CACHE_STATS | |
| ++fCacheMisses; | |
| #endif | |
| GrGLProgram* program = GrGLProgramBuilder::CreateProgram(args, fGpu); | |
| if (nullptr == program) { | |
| return nullptr; | |
| } | |
| int purgeIdx = 0; | |
| if (fCount < kMaxEntries) { | |
| entry = new Entry; | |
| purgeIdx = fCount++; | |
| fEntries[purgeIdx] = entry; | |
| } else { | |
| SkASSERT(fCount == kMaxEntries); | |
| purgeIdx = 0; | |
| for (int i = 1; i < kMaxEntries; ++i) { | |
| if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { | |
| purgeIdx = i; | |
| } | |
| } | |
| entry = fEntries[purgeIdx]; | |
| int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1); | |
| if (fHashTable[purgedHashIdx] == entry) { | |
| fHashTable[purgedHashIdx] = nullptr; | |
| } | |
| } | |
| SkASSERT(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; | |
| } | |
| #ifdef SK_DEBUG | |
| SkASSERT(fEntries[0]->fProgram.get()); | |
| for (int i = 0; i < fCount - 1; ++i) { | |
| SkASSERT(fEntries[i + 1]->fProgram.get()); | |
| const GrProgramDesc& a = fEntries[i]->fProgram->getDesc(); | |
| const GrProgramDesc& b = fEntries[i + 1]->fProgram->getDesc(); | |
| SkASSERT(GrProgramDesc::Less(a, b)); | |
| SkASSERT(!GrProgramDesc::Less(b, a)); | |
| } | |
| #endif | |
| } | |
| fHashTable[hashIdx] = entry; | |
| entry->fLRUStamp = 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; | |
| } | |
| } | |
| ++fCurrLRUStamp; | |
| return SkRef(entry->fProgram.get()); | |
| } |