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 "GrGLPathRendering.h"
     13 #include "glsl/GrGLSLFragmentProcessor.h"
     14 #include "glsl/GrGLSLProgramDataManager.h"
     15 #include "SkRTConf.h"
     16 #include "SkTSearch.h"
     17 
     18 #ifdef PROGRAM_CACHE_STATS
     19 SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false,
     20                 "Display program cache usage.");
     21 #endif
     22 
     23 typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
     24 
     25 struct GrGLGpu::ProgramCache::Entry {
     26 
     27     Entry() : fProgram(nullptr), fLRUStamp(0) {}
     28 
     29     SkAutoTUnref<GrGLProgram>   fProgram;
     30     unsigned int                fLRUStamp;
     31 };
     32 
     33 struct 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 
     45 GrGLGpu::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] = nullptr;
     57     }
     58 }
     59 
     60 GrGLGpu::ProgramCache::~ProgramCache() {
     61     for (int i = 0; i < fCount; ++i){
     62         delete 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 
     80 void GrGLGpu::ProgramCache::reset() {
     81     for (int i = 0; i < fCount; ++i) {
     82         SkASSERT(fEntries[i]->fProgram.get());
     83         fEntries[i]->fProgram->abandon();
     84         delete fEntries[i];
     85         fEntries[i] = nullptr;
     86     }
     87     fCount = 0;
     88 
     89     // zero out hash table
     90     for (int i = 0; i < 1 << kHashBits; i++) {
     91         fHashTable[i] = nullptr;
     92     }
     93 
     94     fCurrLRUStamp = 0;
     95 #ifdef PROGRAM_CACHE_STATS
     96     fTotalRequests = 0;
     97     fCacheMisses = 0;
     98     fHashMisses = 0;
     99 #endif
    100 }
    101 
    102 void GrGLGpu::ProgramCache::abandon() {
    103     this->reset();
    104 }
    105 
    106 int GrGLGpu::ProgramCache::search(const GrProgramDesc& desc) const {
    107     ProgDescLess less;
    108     return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less);
    109 }
    110 
    111 GrGLProgram* GrGLGpu::ProgramCache::refProgram(const DrawArgs& args) {
    112 #ifdef PROGRAM_CACHE_STATS
    113     ++fTotalRequests;
    114 #endif
    115 
    116     Entry* entry = nullptr;
    117 
    118     uint32_t hashIdx = args.fDesc->getChecksum();
    119     hashIdx ^= hashIdx >> 16;
    120     if (kHashBits <= 8) {
    121         hashIdx ^= hashIdx >> 8;
    122     }
    123     hashIdx &=((1 << kHashBits) - 1);
    124     Entry* hashedEntry = fHashTable[hashIdx];
    125     if (hashedEntry && hashedEntry->fProgram->getDesc() == *args.fDesc) {
    126         SkASSERT(hashedEntry->fProgram);
    127         entry = hashedEntry;
    128     }
    129 
    130     int entryIdx;
    131     if (nullptr == entry) {
    132         entryIdx = this->search(*args.fDesc);
    133         if (entryIdx >= 0) {
    134             entry = fEntries[entryIdx];
    135 #ifdef PROGRAM_CACHE_STATS
    136             ++fHashMisses;
    137 #endif
    138         }
    139     }
    140 
    141     if (nullptr == entry) {
    142         // We have a cache miss
    143 #ifdef PROGRAM_CACHE_STATS
    144         ++fCacheMisses;
    145 #endif
    146         GrGLProgram* program = GrGLProgramBuilder::CreateProgram(args, fGpu);
    147         if (nullptr == program) {
    148             return nullptr;
    149         }
    150         int purgeIdx = 0;
    151         if (fCount < kMaxEntries) {
    152             entry = new Entry;
    153             purgeIdx = fCount++;
    154             fEntries[purgeIdx] = entry;
    155         } else {
    156             SkASSERT(fCount == kMaxEntries);
    157             purgeIdx = 0;
    158             for (int i = 1; i < kMaxEntries; ++i) {
    159                 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) {
    160                     purgeIdx = i;
    161                 }
    162             }
    163             entry = fEntries[purgeIdx];
    164             int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1);
    165             if (fHashTable[purgedHashIdx] == entry) {
    166                 fHashTable[purgedHashIdx] = nullptr;
    167             }
    168         }
    169         SkASSERT(fEntries[purgeIdx] == entry);
    170         entry->fProgram.reset(program);
    171         // We need to shift fEntries around so that the entry currently at purgeIdx is placed
    172         // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor).
    173         entryIdx = ~entryIdx;
    174         if (entryIdx < purgeIdx) {
    175             //  Let E and P be the entries at index entryIdx and purgeIdx, respectively.
    176             //  If the entries array looks like this:
    177             //       aaaaEbbbbbPccccc
    178             //  we rearrange it to look like this:
    179             //       aaaaPEbbbbbccccc
    180             size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*);
    181             memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize);
    182             fEntries[entryIdx] = entry;
    183         } else if (purgeIdx < entryIdx) {
    184             //  If the entries array looks like this:
    185             //       aaaaPbbbbbEccccc
    186             //  we rearrange it to look like this:
    187             //       aaaabbbbbPEccccc
    188             size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*);
    189             memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize);
    190             fEntries[entryIdx - 1] = entry;
    191         }
    192 #ifdef SK_DEBUG
    193         SkASSERT(fEntries[0]->fProgram.get());
    194         for (int i = 0; i < fCount - 1; ++i) {
    195             SkASSERT(fEntries[i + 1]->fProgram.get());
    196             const GrProgramDesc& a = fEntries[i]->fProgram->getDesc();
    197             const GrProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
    198             SkASSERT(GrProgramDesc::Less(a, b));
    199             SkASSERT(!GrProgramDesc::Less(b, a));
    200         }
    201 #endif
    202     }
    203 
    204     fHashTable[hashIdx] = entry;
    205     entry->fLRUStamp = fCurrLRUStamp;
    206 
    207     if (SK_MaxU32 == fCurrLRUStamp) {
    208         // wrap around! just trash our LRU, one time hit.
    209         for (int i = 0; i < fCount; ++i) {
    210             fEntries[i]->fLRUStamp = 0;
    211         }
    212     }
    213     ++fCurrLRUStamp;
    214     return SkRef(entry->fProgram.get());
    215 }
    216