2 * Copyright 2011 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
11 #include "GrGLEffect.h"
13 #include "SkTSearch.h"
15 #ifdef PROGRAM_CACHE_STATS
16 SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false,
17 "Display program cache usage.");
20 typedef GrGLUniformManager::UniformHandle UniformHandle;
22 struct GrGpuGL::ProgramCache::Entry {
23 SK_DECLARE_INST_COUNT_ROOT(Entry);
24 Entry() : fProgram(NULL), fLRUStamp(0) {}
26 SkAutoTUnref<GrGLProgram> fProgram;
27 unsigned int fLRUStamp;
30 struct GrGpuGL::ProgramCache::ProgDescLess {
31 bool operator() (const GrGLProgramDesc& desc, const Entry* entry) {
32 SkASSERT(NULL != entry->fProgram.get());
33 return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc());
36 bool operator() (const Entry* entry, const GrGLProgramDesc& desc) {
37 SkASSERT(NULL != entry->fProgram.get());
38 return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc);
42 GrGpuGL::ProgramCache::ProgramCache(GrGpuGL* gpu)
46 #ifdef PROGRAM_CACHE_STATS
52 for (int i = 0; i < 1 << kHashBits; ++i) {
57 GrGpuGL::ProgramCache::~ProgramCache() {
58 for (int i = 0; i < fCount; ++i){
59 SkDELETE(fEntries[i]);
62 #ifdef PROGRAM_CACHE_STATS
64 SkDebugf("--- Program Cache ---\n");
65 SkDebugf("Total requests: %d\n", fTotalRequests);
66 SkDebugf("Cache misses: %d\n", fCacheMisses);
67 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ?
68 100.f * fCacheMisses / fTotalRequests :
70 int cacheHits = fTotalRequests - fCacheMisses;
71 SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f);
72 SkDebugf("---------------------\n");
77 void GrGpuGL::ProgramCache::abandon() {
78 for (int i = 0; i < fCount; ++i) {
79 SkASSERT(NULL != fEntries[i]->fProgram.get());
80 fEntries[i]->fProgram->abandon();
81 fEntries[i]->fProgram.reset(NULL);
86 int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const {
88 return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less);
91 GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc,
92 const GrEffectStage* colorStages[],
93 const GrEffectStage* coverageStages[]) {
94 #ifdef PROGRAM_CACHE_STATS
100 uint32_t hashIdx = desc.getChecksum();
101 hashIdx ^= hashIdx >> 16;
102 if (kHashBits <= 8) {
103 hashIdx ^= hashIdx >> 8;
105 hashIdx &=((1 << kHashBits) - 1);
106 Entry* hashedEntry = fHashTable[hashIdx];
107 if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) {
108 SkASSERT(NULL != hashedEntry->fProgram);
114 entryIdx = this->search(desc);
116 entry = fEntries[entryIdx];
117 #ifdef PROGRAM_CACHE_STATS
124 // We have a cache miss
125 #ifdef PROGRAM_CACHE_STATS
128 GrGLProgram* program = GrGLProgram::Create(fGpu, desc, colorStages, coverageStages);
129 if (NULL == program) {
133 if (fCount < kMaxEntries) {
134 entry = SkNEW(Entry);
136 fEntries[purgeIdx] = entry;
138 SkASSERT(fCount == kMaxEntries);
140 for (int i = 1; i < kMaxEntries; ++i) {
141 if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) {
145 entry = fEntries[purgeIdx];
146 int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1);
147 if (fHashTable[purgedHashIdx] == entry) {
148 fHashTable[purgedHashIdx] = NULL;
151 SkASSERT(fEntries[purgeIdx] == entry);
152 entry->fProgram.reset(program);
153 // We need to shift fEntries around so that the entry currently at purgeIdx is placed
154 // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor).
155 entryIdx = ~entryIdx;
156 if (entryIdx < purgeIdx) {
157 // Let E and P be the entries at index entryIdx and purgeIdx, respectively.
158 // If the entries array looks like this:
160 // we rearrange it to look like this:
162 size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*);
163 memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize);
164 fEntries[entryIdx] = entry;
165 } else if (purgeIdx < entryIdx) {
166 // If the entries array looks like this:
168 // we rearrange it to look like this:
170 size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*);
171 memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize);
172 fEntries[entryIdx - 1] = entry;
175 SkASSERT(NULL != fEntries[0]->fProgram.get());
176 for (int i = 0; i < fCount - 1; ++i) {
177 SkASSERT(NULL != fEntries[i + 1]->fProgram.get());
178 const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc();
179 const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
180 SkASSERT(GrGLProgramDesc::Less(a, b));
181 SkASSERT(!GrGLProgramDesc::Less(b, a));
186 fHashTable[hashIdx] = entry;
187 entry->fLRUStamp = fCurrLRUStamp;
189 if (SK_MaxU32 == fCurrLRUStamp) {
190 // wrap around! just trash our LRU, one time hit.
191 for (int i = 0; i < fCount; ++i) {
192 fEntries[i]->fLRUStamp = 0;
196 return entry->fProgram;
199 ////////////////////////////////////////////////////////////////////////////////
201 void GrGpuGL::abandonResources(){
202 INHERITED::abandonResources();
203 fProgramCache->abandon();
207 ////////////////////////////////////////////////////////////////////////////////
209 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
211 bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
212 const GrDrawState& drawState = this->getDrawState();
214 // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
215 SkASSERT(NULL != drawState.getRenderTarget());
217 if (kStencilPath_DrawType == type) {
218 const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
220 size.set(rt->width(), rt->height());
221 this->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin());
223 this->flushMiscFixedFunctionState();
225 GrBlendCoeff srcCoeff;
226 GrBlendCoeff dstCoeff;
227 GrDrawState::BlendOptFlags blendOpts = drawState.getBlendOpts(false, &srcCoeff, &dstCoeff);
228 if (GrDrawState::kSkipDraw_BlendOptFlag & blendOpts) {
232 SkSTArray<8, const GrEffectStage*, true> colorStages;
233 SkSTArray<8, const GrEffectStage*, true> coverageStages;
234 GrGLProgramDesc desc;
235 GrGLProgramDesc::Build(this->getDrawState(),
246 fCurrentProgram.reset(fProgramCache->getProgram(desc,
248 coverageStages.begin()));
249 if (NULL == fCurrentProgram.get()) {
250 SkDEBUGFAIL("Failed to create program!");
254 SkASSERT((kDrawPath_DrawType != type && kDrawPaths_DrawType != type)
255 || !fCurrentProgram->hasVertexShader());
257 fCurrentProgram.get()->ref();
259 GrGLuint programID = fCurrentProgram->programID();
260 if (fHWProgramID != programID) {
261 GL_CALL(UseProgram(programID));
262 fHWProgramID = programID;
265 fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff);
266 this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
268 fCurrentProgram->setData(blendOpts,
270 coverageStages.begin(),
272 &fSharedGLProgramState);
274 this->flushStencil(type);
275 this->flushScissor();
276 this->flushAAState(type);
278 SkIRect* devRect = NULL;
279 SkIRect devClipBounds;
280 if (drawState.isClipState()) {
281 this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds);
282 devRect = &devClipBounds;
284 // This must come after textures are flushed because a texture may need
285 // to be msaa-resolved (which will modify bound FBO state).
286 this->flushRenderTarget(devRect);
291 void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) {
293 GrGLsizei stride = static_cast<GrGLsizei>(this->getDrawState().getVertexSize());
295 size_t vertexOffsetInBytes = stride * info.startVertex();
297 const GeometryPoolState& geoPoolState = this->getGeomPoolState();
299 GrGLVertexBuffer* vbuf;
300 switch (this->getGeomSrc().fVertexSrc) {
301 case kBuffer_GeometrySrcType:
302 vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
304 case kArray_GeometrySrcType:
305 case kReserved_GeometrySrcType:
306 this->finalizeReservedVertices();
307 vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize;
308 vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
311 vbuf = NULL; // suppress warning
312 SkFAIL("Unknown geometry src type!");
315 SkASSERT(NULL != vbuf);
316 SkASSERT(!vbuf->isLocked());
317 vertexOffsetInBytes += vbuf->baseOffset();
319 GrGLIndexBuffer* ibuf = NULL;
320 if (info.isIndexed()) {
321 SkASSERT(NULL != indexOffsetInBytes);
323 switch (this->getGeomSrc().fIndexSrc) {
324 case kBuffer_GeometrySrcType:
325 *indexOffsetInBytes = 0;
326 ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
328 case kArray_GeometrySrcType:
329 case kReserved_GeometrySrcType:
330 this->finalizeReservedIndices();
331 *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort);
332 ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
335 ibuf = NULL; // suppress warning
336 SkFAIL("Unknown geometry src type!");
339 SkASSERT(NULL != ibuf);
340 SkASSERT(!ibuf->isLocked());
341 *indexOffsetInBytes += ibuf->baseOffset();
343 GrGLAttribArrayState* attribState =
344 fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf);
346 if (fCurrentProgram->hasVertexShader()) {
347 int vertexAttribCount = this->getDrawState().getVertexAttribCount();
348 uint32_t usedAttribArraysMask = 0;
349 const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs();
351 for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount;
352 ++vertexAttribIndex, ++vertexAttrib) {
354 usedAttribArraysMask |= (1 << vertexAttribIndex);
355 GrVertexAttribType attribType = vertexAttrib->fType;
356 attribState->set(this,
359 GrGLAttribTypeToLayout(attribType).fCount,
360 GrGLAttribTypeToLayout(attribType).fType,
361 GrGLAttribTypeToLayout(attribType).fNormalized,
363 reinterpret_cast<GrGLvoid*>(
364 vertexOffsetInBytes + vertexAttrib->fOffset));
366 attribState->disableUnusedArrays(this, usedAttribArraysMask);