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