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