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