GrGLGpuProgramCache.cpp revision 170f90b4576f291879371ecd6ae4bc2b1d85c64a
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 "GrGLPathRendering.h" 13#include "GrOptDrawState.h" 14#include "SkRTConf.h" 15#include "SkTSearch.h" 16 17#ifdef PROGRAM_CACHE_STATS 18SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, 19 "Display program cache usage."); 20#endif 21 22typedef GrGLProgramDataManager::UniformHandle UniformHandle; 23 24struct 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 32struct 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 44GrGpuGL::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 59GrGpuGL::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 79void 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 88int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const { 89 ProgDescLess less; 90 return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); 91} 92 93GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc, 94 const GrEffectStage* geometryProcessor, 95 const GrEffectStage* colorStages[], 96 const GrEffectStage* 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 207bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) { 208 SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState()); 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(); 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 GrEffectStage* geometryProcessor = NULL; 231 SkSTArray<8, const GrEffectStage*, true> colorStages; 232 SkSTArray<8, const GrEffectStage*, 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 fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff); 266 this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); 267 268 fCurrentProgram->setData(*optState.get(), 269 type, 270 geometryProcessor, 271 colorStages.begin(), 272 coverageStages.begin(), 273 dstCopy, 274 &fSharedGLProgramState); 275 } 276 277 GrGLRenderTarget* glRT = static_cast<GrGLRenderTarget*>(optState->getRenderTarget()); 278 this->flushStencil(type); 279 this->flushScissor(glRT->getViewport(), glRT->origin()); 280 this->flushAAState(type); 281 282 SkIRect* devRect = NULL; 283 SkIRect devClipBounds; 284 if (optState->isClipState()) { 285 this->getClip()->getConservativeBounds(optState->getRenderTarget(), &devClipBounds); 286 devRect = &devClipBounds; 287 } 288 // This must come after textures are flushed because a texture may need 289 // to be msaa-resolved (which will modify bound FBO state). 290 this->flushRenderTarget(glRT, devRect); 291 292 return true; 293} 294 295void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) { 296 SkAutoTUnref<GrOptDrawState> optState(this->getDrawState().createOptState()); 297 298 GrGLsizei stride = static_cast<GrGLsizei>(optState->getVertexStride()); 299 300 size_t vertexOffsetInBytes = stride * info.startVertex(); 301 302 const GeometryPoolState& geoPoolState = this->getGeomPoolState(); 303 304 GrGLVertexBuffer* vbuf; 305 switch (this->getGeomSrc().fVertexSrc) { 306 case kBuffer_GeometrySrcType: 307 vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer; 308 break; 309 case kArray_GeometrySrcType: 310 case kReserved_GeometrySrcType: 311 this->finalizeReservedVertices(); 312 vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize; 313 vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer; 314 break; 315 default: 316 vbuf = NULL; // suppress warning 317 SkFAIL("Unknown geometry src type!"); 318 } 319 320 SkASSERT(vbuf); 321 SkASSERT(!vbuf->isMapped()); 322 vertexOffsetInBytes += vbuf->baseOffset(); 323 324 GrGLIndexBuffer* ibuf = NULL; 325 if (info.isIndexed()) { 326 SkASSERT(indexOffsetInBytes); 327 328 switch (this->getGeomSrc().fIndexSrc) { 329 case kBuffer_GeometrySrcType: 330 *indexOffsetInBytes = 0; 331 ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer; 332 break; 333 case kArray_GeometrySrcType: 334 case kReserved_GeometrySrcType: 335 this->finalizeReservedIndices(); 336 *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort); 337 ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer; 338 break; 339 default: 340 ibuf = NULL; // suppress warning 341 SkFAIL("Unknown geometry src type!"); 342 } 343 344 SkASSERT(ibuf); 345 SkASSERT(!ibuf->isMapped()); 346 *indexOffsetInBytes += ibuf->baseOffset(); 347 } 348 GrGLAttribArrayState* attribState = 349 fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf); 350 351 if (fCurrentProgram->hasVertexShader()) { 352 int vertexAttribCount = optState->getVertexAttribCount(); 353 uint32_t usedAttribArraysMask = 0; 354 const GrVertexAttrib* vertexAttrib = optState->getVertexAttribs(); 355 356 for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount; 357 ++vertexAttribIndex, ++vertexAttrib) { 358 usedAttribArraysMask |= (1 << vertexAttribIndex); 359 GrVertexAttribType attribType = vertexAttrib->fType; 360 attribState->set(this, 361 vertexAttribIndex, 362 vbuf, 363 GrGLAttribTypeToLayout(attribType).fCount, 364 GrGLAttribTypeToLayout(attribType).fType, 365 GrGLAttribTypeToLayout(attribType).fNormalized, 366 stride, 367 reinterpret_cast<GrGLvoid*>( 368 vertexOffsetInBytes + vertexAttrib->fOffset)); 369 } 370 attribState->disableUnusedArrays(this, usedAttribArraysMask); 371 } 372} 373