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 "GrGpuGL.h"
      9 
     10 #include "GrProcessor.h"
     11 #include "GrGLProcessor.h"
     12 #include "GrGLPathRendering.h"
     13 #include "GrOptDrawState.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 GrGpuGL::ProgramCache::Entry {
     25     SK_DECLARE_INST_COUNT_ROOT(Entry);
     26     Entry() : fProgram(NULL), fLRUStamp(0) {}
     27 
     28     SkAutoTUnref<GrGLProgram>   fProgram;
     29     unsigned int                fLRUStamp;
     30 };
     31 
     32 struct GrGpuGL::ProgramCache::ProgDescLess {
     33     bool operator() (const GrGLProgramDesc& desc, const Entry* entry) {
     34         SkASSERT(entry->fProgram.get());
     35         return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc());
     36     }
     37 
     38     bool operator() (const Entry* entry, const GrGLProgramDesc& desc) {
     39         SkASSERT(entry->fProgram.get());
     40         return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc);
     41     }
     42 };
     43 
     44 GrGpuGL::ProgramCache::ProgramCache(GrGpuGL* 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 GrGpuGL::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 GrGpuGL::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 GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const {
     89     ProgDescLess less;
     90     return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less);
     91 }
     92 
     93 GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc,
     94                                                const GrGeometryStage* geometryProcessor,
     95                                                const GrFragmentStage* colorStages[],
     96                                                const GrFragmentStage* coverageStages[]) {
     97 #ifdef PROGRAM_CACHE_STATS
     98     ++fTotalRequests;
     99 #endif
    100 
    101     Entry* entry = NULL;
    102 
    103     uint32_t hashIdx = desc.getChecksum();
    104     hashIdx ^= hashIdx >> 16;
    105     if (kHashBits <= 8) {
    106         hashIdx ^= hashIdx >> 8;
    107     }
    108     hashIdx &=((1 << kHashBits) - 1);
    109     Entry* hashedEntry = fHashTable[hashIdx];
    110     if (hashedEntry && hashedEntry->fProgram->getDesc() == desc) {
    111         SkASSERT(hashedEntry->fProgram);
    112         entry = hashedEntry;
    113     }
    114 
    115     int entryIdx;
    116     if (NULL == entry) {
    117         entryIdx = this->search(desc);
    118         if (entryIdx >= 0) {
    119             entry = fEntries[entryIdx];
    120 #ifdef PROGRAM_CACHE_STATS
    121             ++fHashMisses;
    122 #endif
    123         }
    124     }
    125 
    126     if (NULL == entry) {
    127         // We have a cache miss
    128 #ifdef PROGRAM_CACHE_STATS
    129         ++fCacheMisses;
    130 #endif
    131         GrGLProgram* program = GrGLProgram::Create(fGpu, desc, geometryProcessor,
    132                 colorStages, coverageStages);
    133         if (NULL == program) {
    134             return NULL;
    135         }
    136         int purgeIdx = 0;
    137         if (fCount < kMaxEntries) {
    138             entry = SkNEW(Entry);
    139             purgeIdx = fCount++;
    140             fEntries[purgeIdx] = entry;
    141         } else {
    142             SkASSERT(fCount == kMaxEntries);
    143             purgeIdx = 0;
    144             for (int i = 1; i < kMaxEntries; ++i) {
    145                 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) {
    146                     purgeIdx = i;
    147                 }
    148             }
    149             entry = fEntries[purgeIdx];
    150             int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1);
    151             if (fHashTable[purgedHashIdx] == entry) {
    152                 fHashTable[purgedHashIdx] = NULL;
    153             }
    154         }
    155         SkASSERT(fEntries[purgeIdx] == entry);
    156         entry->fProgram.reset(program);
    157         // We need to shift fEntries around so that the entry currently at purgeIdx is placed
    158         // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor).
    159         entryIdx = ~entryIdx;
    160         if (entryIdx < purgeIdx) {
    161             //  Let E and P be the entries at index entryIdx and purgeIdx, respectively.
    162             //  If the entries array looks like this:
    163             //       aaaaEbbbbbPccccc
    164             //  we rearrange it to look like this:
    165             //       aaaaPEbbbbbccccc
    166             size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*);
    167             memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize);
    168             fEntries[entryIdx] = entry;
    169         } else if (purgeIdx < entryIdx) {
    170             //  If the entries array looks like this:
    171             //       aaaaPbbbbbEccccc
    172             //  we rearrange it to look like this:
    173             //       aaaabbbbbPEccccc
    174             size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*);
    175             memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize);
    176             fEntries[entryIdx - 1] = entry;
    177         }
    178 #ifdef SK_DEBUG
    179         SkASSERT(fEntries[0]->fProgram.get());
    180         for (int i = 0; i < fCount - 1; ++i) {
    181             SkASSERT(fEntries[i + 1]->fProgram.get());
    182             const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc();
    183             const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
    184             SkASSERT(GrGLProgramDesc::Less(a, b));
    185             SkASSERT(!GrGLProgramDesc::Less(b, a));
    186         }
    187 #endif
    188     }
    189 
    190     fHashTable[hashIdx] = entry;
    191     entry->fLRUStamp = fCurrLRUStamp;
    192 
    193     if (SK_MaxU32 == fCurrLRUStamp) {
    194         // wrap around! just trash our LRU, one time hit.
    195         for (int i = 0; i < fCount; ++i) {
    196             fEntries[i]->fLRUStamp = 0;
    197         }
    198     }
    199     ++fCurrLRUStamp;
    200     return entry->fProgram;
    201 }
    202 
    203 ////////////////////////////////////////////////////////////////////////////////
    204 
    205 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
    206 
    207 bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
    208     SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState(*this->caps()));
    209 
    210     // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
    211     SkASSERT(optState->getRenderTarget());
    212 
    213     if (kStencilPath_DrawType == type) {
    214         const GrRenderTarget* rt = optState->getRenderTarget();
    215         SkISize size;
    216         size.set(rt->width(), rt->height());
    217         this->glPathRendering()->setProjectionMatrix(optState->getViewMatrix(), size, rt->origin());
    218     } else {
    219         this->flushMiscFixedFunctionState(*optState.get());
    220 
    221         GrBlendCoeff srcCoeff = optState->getSrcBlendCoeff();
    222         GrBlendCoeff dstCoeff = optState->getDstBlendCoeff();
    223 
    224         // In these blend coeff's we end up drawing nothing so we can skip draw all together
    225         if (kZero_GrBlendCoeff == srcCoeff && kOne_GrBlendCoeff == dstCoeff &&
    226             !optState->getStencil().doesWrite()) {
    227             return false;
    228         }
    229 
    230         const GrGeometryStage* geometryProcessor = NULL;
    231         SkSTArray<8, const GrFragmentStage*, true> colorStages;
    232         SkSTArray<8, const GrFragmentStage*, true> coverageStages;
    233         GrGLProgramDesc desc;
    234         if (!GrGLProgramDesc::Build(*optState.get(),
    235                                type,
    236                                srcCoeff,
    237                                dstCoeff,
    238                                this,
    239                                dstCopy,
    240                                &geometryProcessor,
    241                                &colorStages,
    242                                &coverageStages,
    243                                &desc)) {
    244             SkDEBUGFAIL("Failed to generate GL program descriptor");
    245             return false;
    246         }
    247 
    248         fCurrentProgram.reset(fProgramCache->getProgram(desc,
    249                                                         geometryProcessor,
    250                                                         colorStages.begin(),
    251                                                         coverageStages.begin()));
    252         if (NULL == fCurrentProgram.get()) {
    253             SkDEBUGFAIL("Failed to create program!");
    254             return false;
    255         }
    256 
    257         fCurrentProgram.get()->ref();
    258 
    259         GrGLuint programID = fCurrentProgram->programID();
    260         if (fHWProgramID != programID) {
    261             GL_CALL(UseProgram(programID));
    262             fHWProgramID = programID;
    263         }
    264 
    265         this->flushBlend(*optState.get(), kDrawLines_DrawType == type, srcCoeff, dstCoeff);
    266 
    267         fCurrentProgram->setData(*optState.get(),
    268                                  type,
    269                                  geometryProcessor,
    270                                  colorStages.begin(),
    271                                  coverageStages.begin(),
    272                                  dstCopy,
    273                                  &fSharedGLProgramState);
    274     }
    275 
    276     GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(optState->getRenderTarget());
    277     this->flushStencil(type);
    278     this->flushScissor(glRT->getViewport(), glRT->origin());
    279     this->flushAAState(*optState.get(), type);
    280 
    281     SkIRect* devRect = NULL;
    282     SkIRect devClipBounds;
    283     if (optState->isClipState()) {
    284         this->getClip()->getConservativeBounds(optState->getRenderTarget(), &devClipBounds);
    285         devRect = &devClipBounds;
    286     }
    287     // This must come after textures are flushed because a texture may need
    288     // to be msaa-resolved (which will modify bound FBO state).
    289     this->flushRenderTarget(glRT, devRect);
    290 
    291     return true;
    292 }
    293 
    294 void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) {
    295     SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState(*this->caps()));
    296 
    297     GrGLsizei stride = static_cast<GrGLsizei>(optState->getVertexStride());
    298 
    299     size_t vertexOffsetInBytes = stride * info.startVertex();
    300 
    301     const GeometryPoolState& geoPoolState = this->getGeomPoolState();
    302 
    303     GrGLVertexBuffer* vbuf;
    304     switch (this->getGeomSrc().fVertexSrc) {
    305         case kBuffer_GeometrySrcType:
    306             vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
    307             break;
    308         case kArray_GeometrySrcType:
    309         case kReserved_GeometrySrcType:
    310             this->finalizeReservedVertices();
    311             vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize;
    312             vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
    313             break;
    314         default:
    315             vbuf = NULL; // suppress warning
    316             SkFAIL("Unknown geometry src type!");
    317     }
    318 
    319     SkASSERT(vbuf);
    320     SkASSERT(!vbuf->isMapped());
    321     vertexOffsetInBytes += vbuf->baseOffset();
    322 
    323     GrGLIndexBuffer* ibuf = NULL;
    324     if (info.isIndexed()) {
    325         SkASSERT(indexOffsetInBytes);
    326 
    327         switch (this->getGeomSrc().fIndexSrc) {
    328         case kBuffer_GeometrySrcType:
    329             *indexOffsetInBytes = 0;
    330             ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
    331             break;
    332         case kArray_GeometrySrcType:
    333         case kReserved_GeometrySrcType:
    334             this->finalizeReservedIndices();
    335             *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort);
    336             ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
    337             break;
    338         default:
    339             ibuf = NULL; // suppress warning
    340             SkFAIL("Unknown geometry src type!");
    341         }
    342 
    343         SkASSERT(ibuf);
    344         SkASSERT(!ibuf->isMapped());
    345         *indexOffsetInBytes += ibuf->baseOffset();
    346     }
    347     GrGLAttribArrayState* attribState =
    348         fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf);
    349 
    350     if (fCurrentProgram->hasVertexShader()) {
    351         int vertexAttribCount = optState->getVertexAttribCount();
    352         uint32_t usedAttribArraysMask = 0;
    353         const GrVertexAttrib* vertexAttrib = optState->getVertexAttribs();
    354 
    355         for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount;
    356              ++vertexAttribIndex, ++vertexAttrib) {
    357             usedAttribArraysMask |= (1 << vertexAttribIndex);
    358             GrVertexAttribType attribType = vertexAttrib->fType;
    359             attribState->set(this,
    360                              vertexAttribIndex,
    361                              vbuf,
    362                              GrGLAttribTypeToLayout(attribType).fCount,
    363                              GrGLAttribTypeToLayout(attribType).fType,
    364                              GrGLAttribTypeToLayout(attribType).fNormalized,
    365                              stride,
    366                              reinterpret_cast<GrGLvoid*>(
    367                                  vertexOffsetInBytes + vertexAttrib->fOffset));
    368         }
    369         attribState->disableUnusedArrays(this, usedAttribArraysMask);
    370     }
    371 }
    372