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