GrGLGpuProgramCache.cpp revision 873ad0e0b4d67bdc7bad025018f597450e7004c6
1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "GrGLGpu.h" 9 10#include "builders/GrGLProgramBuilder.h" 11#include "GrProcessor.h" 12#include "GrGLProcessor.h" 13#include "GrGLPathRendering.h" 14#include "GrOptDrawState.h" 15#include "SkRTConf.h" 16#include "SkTSearch.h" 17 18#ifdef PROGRAM_CACHE_STATS 19SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, 20 "Display program cache usage."); 21#endif 22 23typedef GrGLProgramDataManager::UniformHandle UniformHandle; 24 25struct GrGLGpu::ProgramCache::Entry { 26 SK_DECLARE_INST_COUNT(Entry); 27 Entry() : fProgram(NULL), fLRUStamp(0) {} 28 29 SkAutoTUnref<GrGLProgram> fProgram; 30 unsigned int fLRUStamp; 31}; 32 33struct GrGLGpu::ProgramCache::ProgDescLess { 34 bool operator() (const GrProgramDesc& desc, const Entry* entry) { 35 SkASSERT(entry->fProgram.get()); 36 return GrProgramDesc::Less(desc, entry->fProgram->getDesc()); 37 } 38 39 bool operator() (const Entry* entry, const GrProgramDesc& desc) { 40 SkASSERT(entry->fProgram.get()); 41 return GrProgramDesc::Less(entry->fProgram->getDesc(), desc); 42 } 43}; 44 45GrGLGpu::ProgramCache::ProgramCache(GrGLGpu* gpu) 46 : fCount(0) 47 , fCurrLRUStamp(0) 48 , fGpu(gpu) 49#ifdef PROGRAM_CACHE_STATS 50 , fTotalRequests(0) 51 , fCacheMisses(0) 52 , fHashMisses(0) 53#endif 54{ 55 for (int i = 0; i < 1 << kHashBits; ++i) { 56 fHashTable[i] = NULL; 57 } 58} 59 60GrGLGpu::ProgramCache::~ProgramCache() { 61 for (int i = 0; i < fCount; ++i){ 62 SkDELETE(fEntries[i]); 63 } 64 // dump stats 65#ifdef PROGRAM_CACHE_STATS 66 if (c_DisplayCache) { 67 SkDebugf("--- Program Cache ---\n"); 68 SkDebugf("Total requests: %d\n", fTotalRequests); 69 SkDebugf("Cache misses: %d\n", fCacheMisses); 70 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? 71 100.f * fCacheMisses / fTotalRequests : 72 0.f); 73 int cacheHits = fTotalRequests - fCacheMisses; 74 SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); 75 SkDebugf("---------------------\n"); 76 } 77#endif 78} 79 80void GrGLGpu::ProgramCache::abandon() { 81 for (int i = 0; i < fCount; ++i) { 82 SkASSERT(fEntries[i]->fProgram.get()); 83 fEntries[i]->fProgram->abandon(); 84 SkDELETE(fEntries[i]); 85 } 86 fCount = 0; 87} 88 89int GrGLGpu::ProgramCache::search(const GrProgramDesc& desc) const { 90 ProgDescLess less; 91 return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); 92} 93 94GrGLProgram* GrGLGpu::ProgramCache::getProgram(const DrawArgs& args) { 95#ifdef PROGRAM_CACHE_STATS 96 ++fTotalRequests; 97#endif 98 99 Entry* entry = NULL; 100 101 uint32_t hashIdx = args.fDesc->getChecksum(); 102 hashIdx ^= hashIdx >> 16; 103 if (kHashBits <= 8) { 104 hashIdx ^= hashIdx >> 8; 105 } 106 hashIdx &=((1 << kHashBits) - 1); 107 Entry* hashedEntry = fHashTable[hashIdx]; 108 if (hashedEntry && hashedEntry->fProgram->getDesc() == *args.fDesc) { 109 SkASSERT(hashedEntry->fProgram); 110 entry = hashedEntry; 111 } 112 113 int entryIdx; 114 if (NULL == entry) { 115 entryIdx = this->search(*args.fDesc); 116 if (entryIdx >= 0) { 117 entry = fEntries[entryIdx]; 118#ifdef PROGRAM_CACHE_STATS 119 ++fHashMisses; 120#endif 121 } 122 } 123 124 if (NULL == entry) { 125 // We have a cache miss 126#ifdef PROGRAM_CACHE_STATS 127 ++fCacheMisses; 128#endif 129 GrGLProgram* program = GrGLProgramBuilder::CreateProgram(args, fGpu); 130 if (NULL == program) { 131 return NULL; 132 } 133 int purgeIdx = 0; 134 if (fCount < kMaxEntries) { 135 entry = SkNEW(Entry); 136 purgeIdx = fCount++; 137 fEntries[purgeIdx] = entry; 138 } else { 139 SkASSERT(fCount == kMaxEntries); 140 purgeIdx = 0; 141 for (int i = 1; i < kMaxEntries; ++i) { 142 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { 143 purgeIdx = i; 144 } 145 } 146 entry = fEntries[purgeIdx]; 147 int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1); 148 if (fHashTable[purgedHashIdx] == entry) { 149 fHashTable[purgedHashIdx] = NULL; 150 } 151 } 152 SkASSERT(fEntries[purgeIdx] == entry); 153 entry->fProgram.reset(program); 154 // We need to shift fEntries around so that the entry currently at purgeIdx is placed 155 // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor). 156 entryIdx = ~entryIdx; 157 if (entryIdx < purgeIdx) { 158 // Let E and P be the entries at index entryIdx and purgeIdx, respectively. 159 // If the entries array looks like this: 160 // aaaaEbbbbbPccccc 161 // we rearrange it to look like this: 162 // aaaaPEbbbbbccccc 163 size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); 164 memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); 165 fEntries[entryIdx] = entry; 166 } else if (purgeIdx < entryIdx) { 167 // If the entries array looks like this: 168 // aaaaPbbbbbEccccc 169 // we rearrange it to look like this: 170 // aaaabbbbbPEccccc 171 size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); 172 memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); 173 fEntries[entryIdx - 1] = entry; 174 } 175#ifdef SK_DEBUG 176 SkASSERT(fEntries[0]->fProgram.get()); 177 for (int i = 0; i < fCount - 1; ++i) { 178 SkASSERT(fEntries[i + 1]->fProgram.get()); 179 const GrProgramDesc& a = fEntries[i]->fProgram->getDesc(); 180 const GrProgramDesc& b = fEntries[i + 1]->fProgram->getDesc(); 181 SkASSERT(GrProgramDesc::Less(a, b)); 182 SkASSERT(!GrProgramDesc::Less(b, a)); 183 } 184#endif 185 } 186 187 fHashTable[hashIdx] = entry; 188 entry->fLRUStamp = fCurrLRUStamp; 189 190 if (SK_MaxU32 == fCurrLRUStamp) { 191 // wrap around! just trash our LRU, one time hit. 192 for (int i = 0; i < fCount; ++i) { 193 fEntries[i]->fLRUStamp = 0; 194 } 195 } 196 ++fCurrLRUStamp; 197 return entry->fProgram; 198} 199