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