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 "GrEffect.h"
     11 #include "GrGLEffect.h"
     12 #include "SkTSearch.h"
     13 
     14 typedef GrGLUniformManager::UniformHandle UniformHandle;
     15 static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
     16 
     17 struct GrGpuGL::ProgramCache::Entry {
     18     SK_DECLARE_INST_COUNT_ROOT(Entry);
     19     Entry() : fProgram(NULL), fLRUStamp(0) {}
     20 
     21     SkAutoTUnref<GrGLProgram>   fProgram;
     22     unsigned int                fLRUStamp;
     23 };
     24 
     25 SK_DEFINE_INST_COUNT(GrGpuGL::ProgramCache::Entry);
     26 
     27 struct GrGpuGL::ProgramCache::ProgDescLess {
     28     bool operator() (const GrGLProgramDesc& desc, const Entry* entry) {
     29         GrAssert(NULL != entry->fProgram.get());
     30         return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc());
     31     }
     32 
     33     bool operator() (const Entry* entry, const GrGLProgramDesc& desc) {
     34         GrAssert(NULL != entry->fProgram.get());
     35         return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc);
     36     }
     37 };
     38 
     39 GrGpuGL::ProgramCache::ProgramCache(const GrGLContext& gl)
     40     : fCount(0)
     41     , fCurrLRUStamp(0)
     42     , fGL(gl)
     43 #ifdef PROGRAM_CACHE_STATS
     44     , fTotalRequests(0)
     45     , fCacheMisses(0)
     46     , fHashMisses(0)
     47 #endif
     48 {
     49     for (int i = 0; i < 1 << kHashBits; ++i) {
     50         fHashTable[i] = NULL;
     51     }
     52 }
     53 
     54 GrGpuGL::ProgramCache::~ProgramCache() {
     55     for (int i = 0; i < fCount; ++i){
     56         SkDELETE(fEntries[i]);
     57     }
     58     // dump stats
     59 #ifdef PROGRAM_CACHE_STATS
     60     SkDebugf("--- Program Cache ---\n");
     61     SkDebugf("Total requests: %d\n", fTotalRequests);
     62     SkDebugf("Cache misses: %d\n", fCacheMisses);
     63     SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ?
     64                                         100.f * fCacheMisses / fTotalRequests :
     65                                         0.f);
     66     int cacheHits = fTotalRequests - fCacheMisses;
     67     SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f);
     68     SkDebugf("---------------------\n");
     69 #endif
     70 }
     71 
     72 void GrGpuGL::ProgramCache::abandon() {
     73     for (int i = 0; i < fCount; ++i) {
     74         GrAssert(NULL != fEntries[i]->fProgram.get());
     75         fEntries[i]->fProgram->abandon();
     76         fEntries[i]->fProgram.reset(NULL);
     77     }
     78     fCount = 0;
     79 }
     80 
     81 int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const {
     82     ProgDescLess less;
     83     return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less);
     84 }
     85 
     86 GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc,
     87                                                const GrEffectStage* colorStages[],
     88                                                const GrEffectStage* coverageStages[]) {
     89 #ifdef PROGRAM_CACHE_STATS
     90     ++fTotalRequests;
     91 #endif
     92 
     93     Entry* entry = NULL;
     94 
     95     uint32_t hashIdx = desc.getChecksum();
     96     hashIdx ^= hashIdx >> 16;
     97     if (kHashBits <= 8) {
     98         hashIdx ^= hashIdx >> 8;
     99     }
    100     hashIdx &=((1 << kHashBits) - 1);
    101     Entry* hashedEntry = fHashTable[hashIdx];
    102     if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) {
    103         GrAssert(NULL != hashedEntry->fProgram);
    104         entry = hashedEntry;
    105     }
    106 
    107     int entryIdx;
    108     if (NULL == entry) {
    109         entryIdx = this->search(desc);
    110         if (entryIdx >= 0) {
    111             entry = fEntries[entryIdx];
    112 #ifdef PROGRAM_CACHE_STATS
    113             ++fHashMisses;
    114 #endif
    115         }
    116     }
    117 
    118     if (NULL == entry) {
    119         // We have a cache miss
    120 #ifdef PROGRAM_CACHE_STATS
    121         ++fCacheMisses;
    122 #endif
    123         GrGLProgram* program = GrGLProgram::Create(fGL, desc, colorStages, coverageStages);
    124         if (NULL == program) {
    125             return NULL;
    126         }
    127         int purgeIdx = 0;
    128         if (fCount < kMaxEntries) {
    129             entry = SkNEW(Entry);
    130             purgeIdx = fCount++;
    131             fEntries[purgeIdx] = entry;
    132         } else {
    133             GrAssert(fCount == kMaxEntries);
    134             purgeIdx = 0;
    135             for (int i = 1; i < kMaxEntries; ++i) {
    136                 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) {
    137                     purgeIdx = i;
    138                 }
    139             }
    140             entry = fEntries[purgeIdx];
    141             int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1);
    142             if (fHashTable[purgedHashIdx] == entry) {
    143                 fHashTable[purgedHashIdx] = NULL;
    144             }
    145         }
    146         GrAssert(fEntries[purgeIdx] == entry);
    147         entry->fProgram.reset(program);
    148         // We need to shift fEntries around so that the entry currently at purgeIdx is placed
    149         // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor).
    150         entryIdx = ~entryIdx;
    151         if (entryIdx < purgeIdx) {
    152             //  Let E and P be the entries at index entryIdx and purgeIdx, respectively.
    153             //  If the entries array looks like this:
    154             //       aaaaEbbbbbPccccc
    155             //  we rearrange it to look like this:
    156             //       aaaaPEbbbbbccccc
    157             size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*);
    158             memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize);
    159             fEntries[entryIdx] = entry;
    160         } else if (purgeIdx < entryIdx) {
    161             //  If the entries array looks like this:
    162             //       aaaaPbbbbbEccccc
    163             //  we rearrange it to look like this:
    164             //       aaaabbbbbPEccccc
    165             size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*);
    166             memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize);
    167             fEntries[entryIdx - 1] = entry;
    168         }
    169 #if GR_DEBUG
    170         GrAssert(NULL != fEntries[0]->fProgram.get());
    171         for (int i = 0; i < fCount - 1; ++i) {
    172             GrAssert(NULL != fEntries[i + 1]->fProgram.get());
    173             const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc();
    174             const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
    175             GrAssert(GrGLProgramDesc::Less(a, b));
    176             GrAssert(!GrGLProgramDesc::Less(b, a));
    177         }
    178 #endif
    179     }
    180 
    181     fHashTable[hashIdx] = entry;
    182     entry->fLRUStamp = fCurrLRUStamp;
    183 
    184     if (SK_MaxU32 == fCurrLRUStamp) {
    185         // wrap around! just trash our LRU, one time hit.
    186         for (int i = 0; i < fCount; ++i) {
    187             fEntries[i]->fLRUStamp = 0;
    188         }
    189     }
    190     ++fCurrLRUStamp;
    191     return entry->fProgram;
    192 }
    193 
    194 ////////////////////////////////////////////////////////////////////////////////
    195 
    196 void GrGpuGL::abandonResources(){
    197     INHERITED::abandonResources();
    198     fProgramCache->abandon();
    199     fHWProgramID = 0;
    200 }
    201 
    202 ////////////////////////////////////////////////////////////////////////////////
    203 
    204 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
    205 
    206 void GrGpuGL::flushPathStencilMatrix() {
    207     const SkMatrix& viewMatrix = this->getDrawState().getViewMatrix();
    208     const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
    209     SkISize size;
    210     size.set(rt->width(), rt->height());
    211     const SkMatrix& vm = this->getDrawState().getViewMatrix();
    212 
    213     if (fHWPathStencilMatrixState.fRenderTargetOrigin != rt->origin() ||
    214         !fHWPathStencilMatrixState.fViewMatrix.cheapEqualTo(viewMatrix) ||
    215         fHWPathStencilMatrixState.fRenderTargetSize!= size) {
    216         // rescale the coords from skia's "device" coords to GL's normalized coords,
    217         // and perform a y-flip if required.
    218         SkMatrix m;
    219         if (kBottomLeft_GrSurfaceOrigin == rt->origin()) {
    220             m.setScale(SkIntToScalar(2) / rt->width(), SkIntToScalar(-2) / rt->height());
    221             m.postTranslate(-SK_Scalar1, SK_Scalar1);
    222         } else {
    223             m.setScale(SkIntToScalar(2) / rt->width(), SkIntToScalar(2) / rt->height());
    224             m.postTranslate(-SK_Scalar1, -SK_Scalar1);
    225         }
    226         m.preConcat(vm);
    227 
    228         // GL wants a column-major 4x4.
    229         GrGLfloat mv[]  = {
    230             // col 0
    231             SkScalarToFloat(m[SkMatrix::kMScaleX]),
    232             SkScalarToFloat(m[SkMatrix::kMSkewY]),
    233             0,
    234             SkScalarToFloat(m[SkMatrix::kMPersp0]),
    235 
    236             // col 1
    237             SkScalarToFloat(m[SkMatrix::kMSkewX]),
    238             SkScalarToFloat(m[SkMatrix::kMScaleY]),
    239             0,
    240             SkScalarToFloat(m[SkMatrix::kMPersp1]),
    241 
    242             // col 2
    243             0, 0, 0, 0,
    244 
    245             // col3
    246             SkScalarToFloat(m[SkMatrix::kMTransX]),
    247             SkScalarToFloat(m[SkMatrix::kMTransY]),
    248             0.0f,
    249             SkScalarToFloat(m[SkMatrix::kMPersp2])
    250         };
    251         GL_CALL(MatrixMode(GR_GL_PROJECTION));
    252         GL_CALL(LoadMatrixf(mv));
    253         fHWPathStencilMatrixState.fViewMatrix = vm;
    254         fHWPathStencilMatrixState.fRenderTargetSize = size;
    255         fHWPathStencilMatrixState.fRenderTargetOrigin = rt->origin();
    256     }
    257 }
    258 
    259 bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
    260     const GrDrawState& drawState = this->getDrawState();
    261 
    262     // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
    263     GrAssert(NULL != drawState.getRenderTarget());
    264 
    265     if (kStencilPath_DrawType == type) {
    266         this->flushPathStencilMatrix();
    267     } else {
    268         this->flushMiscFixedFunctionState();
    269 
    270         GrBlendCoeff srcCoeff;
    271         GrBlendCoeff dstCoeff;
    272         GrDrawState::BlendOptFlags blendOpts = drawState.getBlendOpts(false, &srcCoeff, &dstCoeff);
    273         if (GrDrawState::kSkipDraw_BlendOptFlag & blendOpts) {
    274             return false;
    275         }
    276 
    277         SkSTArray<8, const GrEffectStage*, true> colorStages;
    278         SkSTArray<8, const GrEffectStage*, true> coverageStages;
    279         GrGLProgramDesc desc;
    280         GrGLProgramDesc::Build(this->getDrawState(),
    281                                kDrawPoints_DrawType == type,
    282                                blendOpts,
    283                                srcCoeff,
    284                                dstCoeff,
    285                                this,
    286                                dstCopy,
    287                                &colorStages,
    288                                &coverageStages,
    289                                &desc);
    290 
    291         fCurrentProgram.reset(fProgramCache->getProgram(desc,
    292                                                         colorStages.begin(),
    293                                                         coverageStages.begin()));
    294         if (NULL == fCurrentProgram.get()) {
    295             GrAssert(!"Failed to create program!");
    296             return false;
    297         }
    298         fCurrentProgram.get()->ref();
    299 
    300         GrGLuint programID = fCurrentProgram->programID();
    301         if (fHWProgramID != programID) {
    302             GL_CALL(UseProgram(programID));
    303             fHWProgramID = programID;
    304         }
    305 
    306         fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff);
    307         this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
    308 
    309         fCurrentProgram->setData(this,
    310                                  blendOpts,
    311                                  colorStages.begin(),
    312                                  coverageStages.begin(),
    313                                  dstCopy,
    314                                  &fSharedGLProgramState);
    315     }
    316     this->flushStencil(type);
    317     this->flushScissor();
    318     this->flushAAState(type);
    319 
    320     SkIRect* devRect = NULL;
    321     SkIRect devClipBounds;
    322     if (drawState.isClipState()) {
    323         this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds);
    324         devRect = &devClipBounds;
    325     }
    326     // This must come after textures are flushed because a texture may need
    327     // to be msaa-resolved (which will modify bound FBO state).
    328     this->flushRenderTarget(devRect);
    329 
    330     return true;
    331 }
    332 
    333 void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) {
    334 
    335     GrGLsizei stride = this->getDrawState().getVertexSize();
    336 
    337     size_t vertexOffsetInBytes = stride * info.startVertex();
    338 
    339     const GeometryPoolState& geoPoolState = this->getGeomPoolState();
    340 
    341     GrGLVertexBuffer* vbuf;
    342     switch (this->getGeomSrc().fVertexSrc) {
    343         case kBuffer_GeometrySrcType:
    344             vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
    345             break;
    346         case kArray_GeometrySrcType:
    347         case kReserved_GeometrySrcType:
    348             this->finalizeReservedVertices();
    349             vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize;
    350             vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
    351             break;
    352         default:
    353             vbuf = NULL; // suppress warning
    354             GrCrash("Unknown geometry src type!");
    355     }
    356 
    357     GrAssert(NULL != vbuf);
    358     GrAssert(!vbuf->isLocked());
    359     vertexOffsetInBytes += vbuf->baseOffset();
    360 
    361     GrGLIndexBuffer* ibuf = NULL;
    362     if (info.isIndexed()) {
    363         GrAssert(NULL != indexOffsetInBytes);
    364 
    365         switch (this->getGeomSrc().fIndexSrc) {
    366         case kBuffer_GeometrySrcType:
    367             *indexOffsetInBytes = 0;
    368             ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
    369             break;
    370         case kArray_GeometrySrcType:
    371         case kReserved_GeometrySrcType:
    372             this->finalizeReservedIndices();
    373             *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort);
    374             ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
    375             break;
    376         default:
    377             ibuf = NULL; // suppress warning
    378             GrCrash("Unknown geometry src type!");
    379         }
    380 
    381         GrAssert(NULL != ibuf);
    382         GrAssert(!ibuf->isLocked());
    383         *indexOffsetInBytes += ibuf->baseOffset();
    384     }
    385     GrGLAttribArrayState* attribState =
    386         fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf);
    387 
    388     uint32_t usedAttribArraysMask = 0;
    389     const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs();
    390     int vertexAttribCount = this->getDrawState().getVertexAttribCount();
    391     for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount;
    392          ++vertexAttribIndex, ++vertexAttrib) {
    393 
    394         usedAttribArraysMask |= (1 << vertexAttribIndex);
    395         GrVertexAttribType attribType = vertexAttrib->fType;
    396         attribState->set(this,
    397                          vertexAttribIndex,
    398                          vbuf,
    399                          GrGLAttribTypeToLayout(attribType).fCount,
    400                          GrGLAttribTypeToLayout(attribType).fType,
    401                          GrGLAttribTypeToLayout(attribType).fNormalized,
    402                          stride,
    403                          reinterpret_cast<GrGLvoid*>(
    404                          vertexOffsetInBytes + vertexAttrib->fOffset));
    405     }
    406 
    407     attribState->disableUnusedAttribArrays(this, usedAttribArraysMask);
    408 }
    409