michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrGpuGL.h" michael@0: michael@0: #include "GrEffect.h" michael@0: #include "GrGLEffect.h" michael@0: #include "SkRTConf.h" michael@0: #include "SkTSearch.h" michael@0: michael@0: #ifdef PROGRAM_CACHE_STATS michael@0: SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false, michael@0: "Display program cache usage."); michael@0: #endif michael@0: michael@0: typedef GrGLUniformManager::UniformHandle UniformHandle; michael@0: michael@0: struct GrGpuGL::ProgramCache::Entry { michael@0: SK_DECLARE_INST_COUNT_ROOT(Entry); michael@0: Entry() : fProgram(NULL), fLRUStamp(0) {} michael@0: michael@0: SkAutoTUnref fProgram; michael@0: unsigned int fLRUStamp; michael@0: }; michael@0: michael@0: struct GrGpuGL::ProgramCache::ProgDescLess { michael@0: bool operator() (const GrGLProgramDesc& desc, const Entry* entry) { michael@0: SkASSERT(NULL != entry->fProgram.get()); michael@0: return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc()); michael@0: } michael@0: michael@0: bool operator() (const Entry* entry, const GrGLProgramDesc& desc) { michael@0: SkASSERT(NULL != entry->fProgram.get()); michael@0: return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc); michael@0: } michael@0: }; michael@0: michael@0: GrGpuGL::ProgramCache::ProgramCache(GrGpuGL* gpu) michael@0: : fCount(0) michael@0: , fCurrLRUStamp(0) michael@0: , fGpu(gpu) michael@0: #ifdef PROGRAM_CACHE_STATS michael@0: , fTotalRequests(0) michael@0: , fCacheMisses(0) michael@0: , fHashMisses(0) michael@0: #endif michael@0: { michael@0: for (int i = 0; i < 1 << kHashBits; ++i) { michael@0: fHashTable[i] = NULL; michael@0: } michael@0: } michael@0: michael@0: GrGpuGL::ProgramCache::~ProgramCache() { michael@0: for (int i = 0; i < fCount; ++i){ michael@0: SkDELETE(fEntries[i]); michael@0: } michael@0: // dump stats michael@0: #ifdef PROGRAM_CACHE_STATS michael@0: if (c_DisplayCache) { michael@0: SkDebugf("--- Program Cache ---\n"); michael@0: SkDebugf("Total requests: %d\n", fTotalRequests); michael@0: SkDebugf("Cache misses: %d\n", fCacheMisses); michael@0: SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? michael@0: 100.f * fCacheMisses / fTotalRequests : michael@0: 0.f); michael@0: int cacheHits = fTotalRequests - fCacheMisses; michael@0: SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); michael@0: SkDebugf("---------------------\n"); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void GrGpuGL::ProgramCache::abandon() { michael@0: for (int i = 0; i < fCount; ++i) { michael@0: SkASSERT(NULL != fEntries[i]->fProgram.get()); michael@0: fEntries[i]->fProgram->abandon(); michael@0: fEntries[i]->fProgram.reset(NULL); michael@0: } michael@0: fCount = 0; michael@0: } michael@0: michael@0: int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const { michael@0: ProgDescLess less; michael@0: return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); michael@0: } michael@0: michael@0: GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc, michael@0: const GrEffectStage* colorStages[], michael@0: const GrEffectStage* coverageStages[]) { michael@0: #ifdef PROGRAM_CACHE_STATS michael@0: ++fTotalRequests; michael@0: #endif michael@0: michael@0: Entry* entry = NULL; michael@0: michael@0: uint32_t hashIdx = desc.getChecksum(); michael@0: hashIdx ^= hashIdx >> 16; michael@0: if (kHashBits <= 8) { michael@0: hashIdx ^= hashIdx >> 8; michael@0: } michael@0: hashIdx &=((1 << kHashBits) - 1); michael@0: Entry* hashedEntry = fHashTable[hashIdx]; michael@0: if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) { michael@0: SkASSERT(NULL != hashedEntry->fProgram); michael@0: entry = hashedEntry; michael@0: } michael@0: michael@0: int entryIdx; michael@0: if (NULL == entry) { michael@0: entryIdx = this->search(desc); michael@0: if (entryIdx >= 0) { michael@0: entry = fEntries[entryIdx]; michael@0: #ifdef PROGRAM_CACHE_STATS michael@0: ++fHashMisses; michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: if (NULL == entry) { michael@0: // We have a cache miss michael@0: #ifdef PROGRAM_CACHE_STATS michael@0: ++fCacheMisses; michael@0: #endif michael@0: GrGLProgram* program = GrGLProgram::Create(fGpu, desc, colorStages, coverageStages); michael@0: if (NULL == program) { michael@0: return NULL; michael@0: } michael@0: int purgeIdx = 0; michael@0: if (fCount < kMaxEntries) { michael@0: entry = SkNEW(Entry); michael@0: purgeIdx = fCount++; michael@0: fEntries[purgeIdx] = entry; michael@0: } else { michael@0: SkASSERT(fCount == kMaxEntries); michael@0: purgeIdx = 0; michael@0: for (int i = 1; i < kMaxEntries; ++i) { michael@0: if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { michael@0: purgeIdx = i; michael@0: } michael@0: } michael@0: entry = fEntries[purgeIdx]; michael@0: int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1); michael@0: if (fHashTable[purgedHashIdx] == entry) { michael@0: fHashTable[purgedHashIdx] = NULL; michael@0: } michael@0: } michael@0: SkASSERT(fEntries[purgeIdx] == entry); michael@0: entry->fProgram.reset(program); michael@0: // We need to shift fEntries around so that the entry currently at purgeIdx is placed michael@0: // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor). michael@0: entryIdx = ~entryIdx; michael@0: if (entryIdx < purgeIdx) { michael@0: // Let E and P be the entries at index entryIdx and purgeIdx, respectively. michael@0: // If the entries array looks like this: michael@0: // aaaaEbbbbbPccccc michael@0: // we rearrange it to look like this: michael@0: // aaaaPEbbbbbccccc michael@0: size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); michael@0: memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); michael@0: fEntries[entryIdx] = entry; michael@0: } else if (purgeIdx < entryIdx) { michael@0: // If the entries array looks like this: michael@0: // aaaaPbbbbbEccccc michael@0: // we rearrange it to look like this: michael@0: // aaaabbbbbPEccccc michael@0: size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); michael@0: memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); michael@0: fEntries[entryIdx - 1] = entry; michael@0: } michael@0: #ifdef SK_DEBUG michael@0: SkASSERT(NULL != fEntries[0]->fProgram.get()); michael@0: for (int i = 0; i < fCount - 1; ++i) { michael@0: SkASSERT(NULL != fEntries[i + 1]->fProgram.get()); michael@0: const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc(); michael@0: const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc(); michael@0: SkASSERT(GrGLProgramDesc::Less(a, b)); michael@0: SkASSERT(!GrGLProgramDesc::Less(b, a)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: fHashTable[hashIdx] = entry; michael@0: entry->fLRUStamp = fCurrLRUStamp; michael@0: michael@0: if (SK_MaxU32 == fCurrLRUStamp) { michael@0: // wrap around! just trash our LRU, one time hit. michael@0: for (int i = 0; i < fCount; ++i) { michael@0: fEntries[i]->fLRUStamp = 0; michael@0: } michael@0: } michael@0: ++fCurrLRUStamp; michael@0: return entry->fProgram; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrGpuGL::abandonResources(){ michael@0: INHERITED::abandonResources(); michael@0: fProgramCache->abandon(); michael@0: fHWProgramID = 0; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X) michael@0: michael@0: bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) { michael@0: const GrDrawState& drawState = this->getDrawState(); michael@0: michael@0: // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true. michael@0: SkASSERT(NULL != drawState.getRenderTarget()); michael@0: michael@0: if (kStencilPath_DrawType == type) { michael@0: const GrRenderTarget* rt = this->getDrawState().getRenderTarget(); michael@0: SkISize size; michael@0: size.set(rt->width(), rt->height()); michael@0: this->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin()); michael@0: } else { michael@0: this->flushMiscFixedFunctionState(); michael@0: michael@0: GrBlendCoeff srcCoeff; michael@0: GrBlendCoeff dstCoeff; michael@0: GrDrawState::BlendOptFlags blendOpts = drawState.getBlendOpts(false, &srcCoeff, &dstCoeff); michael@0: if (GrDrawState::kSkipDraw_BlendOptFlag & blendOpts) { michael@0: return false; michael@0: } michael@0: michael@0: SkSTArray<8, const GrEffectStage*, true> colorStages; michael@0: SkSTArray<8, const GrEffectStage*, true> coverageStages; michael@0: GrGLProgramDesc desc; michael@0: GrGLProgramDesc::Build(this->getDrawState(), michael@0: kDrawPoints_DrawType == type, michael@0: blendOpts, michael@0: srcCoeff, michael@0: dstCoeff, michael@0: this, michael@0: dstCopy, michael@0: &colorStages, michael@0: &coverageStages, michael@0: &desc); michael@0: michael@0: fCurrentProgram.reset(fProgramCache->getProgram(desc, michael@0: colorStages.begin(), michael@0: coverageStages.begin())); michael@0: if (NULL == fCurrentProgram.get()) { michael@0: SkDEBUGFAIL("Failed to create program!"); michael@0: return false; michael@0: } michael@0: michael@0: SkASSERT(kDrawPath_DrawType != type || !fCurrentProgram->hasVertexShader()); michael@0: michael@0: fCurrentProgram.get()->ref(); michael@0: michael@0: GrGLuint programID = fCurrentProgram->programID(); michael@0: if (fHWProgramID != programID) { michael@0: GL_CALL(UseProgram(programID)); michael@0: fHWProgramID = programID; michael@0: } michael@0: michael@0: fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff); michael@0: this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); michael@0: michael@0: fCurrentProgram->setData(blendOpts, michael@0: colorStages.begin(), michael@0: coverageStages.begin(), michael@0: dstCopy, michael@0: &fSharedGLProgramState); michael@0: } michael@0: this->flushStencil(type); michael@0: this->flushScissor(); michael@0: this->flushAAState(type); michael@0: michael@0: SkIRect* devRect = NULL; michael@0: SkIRect devClipBounds; michael@0: if (drawState.isClipState()) { michael@0: this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds); michael@0: devRect = &devClipBounds; michael@0: } michael@0: // This must come after textures are flushed because a texture may need michael@0: // to be msaa-resolved (which will modify bound FBO state). michael@0: this->flushRenderTarget(devRect); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) { michael@0: michael@0: GrGLsizei stride = static_cast(this->getDrawState().getVertexSize()); michael@0: michael@0: size_t vertexOffsetInBytes = stride * info.startVertex(); michael@0: michael@0: const GeometryPoolState& geoPoolState = this->getGeomPoolState(); michael@0: michael@0: GrGLVertexBuffer* vbuf; michael@0: switch (this->getGeomSrc().fVertexSrc) { michael@0: case kBuffer_GeometrySrcType: michael@0: vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer; michael@0: break; michael@0: case kArray_GeometrySrcType: michael@0: case kReserved_GeometrySrcType: michael@0: this->finalizeReservedVertices(); michael@0: vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize; michael@0: vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer; michael@0: break; michael@0: default: michael@0: vbuf = NULL; // suppress warning michael@0: GrCrash("Unknown geometry src type!"); michael@0: } michael@0: michael@0: SkASSERT(NULL != vbuf); michael@0: SkASSERT(!vbuf->isLocked()); michael@0: vertexOffsetInBytes += vbuf->baseOffset(); michael@0: michael@0: GrGLIndexBuffer* ibuf = NULL; michael@0: if (info.isIndexed()) { michael@0: SkASSERT(NULL != indexOffsetInBytes); michael@0: michael@0: switch (this->getGeomSrc().fIndexSrc) { michael@0: case kBuffer_GeometrySrcType: michael@0: *indexOffsetInBytes = 0; michael@0: ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer; michael@0: break; michael@0: case kArray_GeometrySrcType: michael@0: case kReserved_GeometrySrcType: michael@0: this->finalizeReservedIndices(); michael@0: *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort); michael@0: ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer; michael@0: break; michael@0: default: michael@0: ibuf = NULL; // suppress warning michael@0: GrCrash("Unknown geometry src type!"); michael@0: } michael@0: michael@0: SkASSERT(NULL != ibuf); michael@0: SkASSERT(!ibuf->isLocked()); michael@0: *indexOffsetInBytes += ibuf->baseOffset(); michael@0: } michael@0: GrGLAttribArrayState* attribState = michael@0: fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf); michael@0: michael@0: if (!fCurrentProgram->hasVertexShader()) { michael@0: int posIdx = this->getDrawState().positionAttributeIndex(); michael@0: const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs() + posIdx; michael@0: GrVertexAttribType attribType = vertexAttrib->fType; michael@0: SkASSERT(!GrGLAttribTypeToLayout(attribType).fNormalized); michael@0: SkASSERT(GrGLAttribTypeToLayout(attribType).fCount == 2); michael@0: michael@0: // Attrib at location 0 is defined to be bound to vertex in fixed-function pipe. Asserts michael@0: // above should make sure position attribute goes to location 0 when below code is executed. michael@0: michael@0: attribState->set(this, michael@0: 0, michael@0: vbuf, michael@0: GrGLAttribTypeToLayout(attribType).fCount, michael@0: GrGLAttribTypeToLayout(attribType).fType, michael@0: GrGLAttribTypeToLayout(attribType).fNormalized, michael@0: stride, michael@0: reinterpret_cast( michael@0: vertexOffsetInBytes + vertexAttrib->fOffset)); michael@0: attribState->disableUnusedArrays(this, 1); michael@0: } else { michael@0: int vertexAttribCount = this->getDrawState().getVertexAttribCount(); michael@0: uint32_t usedAttribArraysMask = 0; michael@0: const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs(); michael@0: michael@0: for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount; michael@0: ++vertexAttribIndex, ++vertexAttrib) { michael@0: michael@0: usedAttribArraysMask |= (1 << vertexAttribIndex); michael@0: GrVertexAttribType attribType = vertexAttrib->fType; michael@0: attribState->set(this, michael@0: vertexAttribIndex, michael@0: vbuf, michael@0: GrGLAttribTypeToLayout(attribType).fCount, michael@0: GrGLAttribTypeToLayout(attribType).fType, michael@0: GrGLAttribTypeToLayout(attribType).fNormalized, michael@0: stride, michael@0: reinterpret_cast( michael@0: vertexOffsetInBytes + vertexAttrib->fOffset)); michael@0: } michael@0: attribState->disableUnusedArrays(this, usedAttribArraysMask); michael@0: } michael@0: }