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 "SkRTConf.h"
13#include "GrGLNameAllocator.h"
14#include "SkTSearch.h"
15
16#ifdef PROGRAM_CACHE_STATS
17SK_CONF_DECLARE(bool, c_DisplayCache, "gpu.displayCache", false,
18                "Display program cache usage.");
19#endif
20
21typedef GrGLUniformManager::UniformHandle UniformHandle;
22
23struct GrGpuGL::ProgramCache::Entry {
24    SK_DECLARE_INST_COUNT_ROOT(Entry);
25    Entry() : fProgram(NULL), fLRUStamp(0) {}
26
27    SkAutoTUnref<GrGLProgram>   fProgram;
28    unsigned int                fLRUStamp;
29};
30
31struct GrGpuGL::ProgramCache::ProgDescLess {
32    bool operator() (const GrGLProgramDesc& desc, const Entry* entry) {
33        SkASSERT(NULL != entry->fProgram.get());
34        return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc());
35    }
36
37    bool operator() (const Entry* entry, const GrGLProgramDesc& desc) {
38        SkASSERT(NULL != entry->fProgram.get());
39        return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc);
40    }
41};
42
43GrGpuGL::ProgramCache::ProgramCache(GrGpuGL* gpu)
44    : fCount(0)
45    , fCurrLRUStamp(0)
46    , fGpu(gpu)
47#ifdef PROGRAM_CACHE_STATS
48    , fTotalRequests(0)
49    , fCacheMisses(0)
50    , fHashMisses(0)
51#endif
52{
53    for (int i = 0; i < 1 << kHashBits; ++i) {
54        fHashTable[i] = NULL;
55    }
56}
57
58GrGpuGL::ProgramCache::~ProgramCache() {
59    for (int i = 0; i < fCount; ++i){
60        SkDELETE(fEntries[i]);
61    }
62    // dump stats
63#ifdef PROGRAM_CACHE_STATS
64    if (c_DisplayCache) {
65        SkDebugf("--- Program Cache ---\n");
66        SkDebugf("Total requests: %d\n", fTotalRequests);
67        SkDebugf("Cache misses: %d\n", fCacheMisses);
68        SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ?
69                                            100.f * fCacheMisses / fTotalRequests :
70                                            0.f);
71        int cacheHits = fTotalRequests - fCacheMisses;
72        SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f);
73        SkDebugf("---------------------\n");
74    }
75#endif
76}
77
78void GrGpuGL::ProgramCache::abandon() {
79    for (int i = 0; i < fCount; ++i) {
80        SkASSERT(NULL != fEntries[i]->fProgram.get());
81        fEntries[i]->fProgram->abandon();
82        fEntries[i]->fProgram.reset(NULL);
83    }
84    fCount = 0;
85}
86
87int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const {
88    ProgDescLess less;
89    return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less);
90}
91
92GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc,
93                                               const GrEffectStage* colorStages[],
94                                               const GrEffectStage* coverageStages[]) {
95#ifdef PROGRAM_CACHE_STATS
96    ++fTotalRequests;
97#endif
98
99    Entry* entry = NULL;
100
101    uint32_t hashIdx = desc.getChecksum();
102    hashIdx ^= hashIdx >> 16;
103    if (kHashBits <= 8) {
104        hashIdx ^= hashIdx >> 8;
105    }
106    hashIdx &=((1 << kHashBits) - 1);
107    Entry* hashedEntry = fHashTable[hashIdx];
108    if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) {
109        SkASSERT(NULL != hashedEntry->fProgram);
110        entry = hashedEntry;
111    }
112
113    int entryIdx;
114    if (NULL == entry) {
115        entryIdx = this->search(desc);
116        if (entryIdx >= 0) {
117            entry = fEntries[entryIdx];
118#ifdef PROGRAM_CACHE_STATS
119            ++fHashMisses;
120#endif
121        }
122    }
123
124    if (NULL == entry) {
125        // We have a cache miss
126#ifdef PROGRAM_CACHE_STATS
127        ++fCacheMisses;
128#endif
129        GrGLProgram* program = GrGLProgram::Create(fGpu, desc, colorStages, coverageStages);
130        if (NULL == program) {
131            return NULL;
132        }
133        int purgeIdx = 0;
134        if (fCount < kMaxEntries) {
135            entry = SkNEW(Entry);
136            purgeIdx = fCount++;
137            fEntries[purgeIdx] = entry;
138        } else {
139            SkASSERT(fCount == kMaxEntries);
140            purgeIdx = 0;
141            for (int i = 1; i < kMaxEntries; ++i) {
142                if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) {
143                    purgeIdx = i;
144                }
145            }
146            entry = fEntries[purgeIdx];
147            int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1);
148            if (fHashTable[purgedHashIdx] == entry) {
149                fHashTable[purgedHashIdx] = NULL;
150            }
151        }
152        SkASSERT(fEntries[purgeIdx] == entry);
153        entry->fProgram.reset(program);
154        // We need to shift fEntries around so that the entry currently at purgeIdx is placed
155        // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor).
156        entryIdx = ~entryIdx;
157        if (entryIdx < purgeIdx) {
158            //  Let E and P be the entries at index entryIdx and purgeIdx, respectively.
159            //  If the entries array looks like this:
160            //       aaaaEbbbbbPccccc
161            //  we rearrange it to look like this:
162            //       aaaaPEbbbbbccccc
163            size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*);
164            memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize);
165            fEntries[entryIdx] = entry;
166        } else if (purgeIdx < entryIdx) {
167            //  If the entries array looks like this:
168            //       aaaaPbbbbbEccccc
169            //  we rearrange it to look like this:
170            //       aaaabbbbbPEccccc
171            size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*);
172            memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize);
173            fEntries[entryIdx - 1] = entry;
174        }
175#ifdef SK_DEBUG
176        SkASSERT(NULL != fEntries[0]->fProgram.get());
177        for (int i = 0; i < fCount - 1; ++i) {
178            SkASSERT(NULL != fEntries[i + 1]->fProgram.get());
179            const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc();
180            const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc();
181            SkASSERT(GrGLProgramDesc::Less(a, b));
182            SkASSERT(!GrGLProgramDesc::Less(b, a));
183        }
184#endif
185    }
186
187    fHashTable[hashIdx] = entry;
188    entry->fLRUStamp = fCurrLRUStamp;
189
190    if (SK_MaxU32 == fCurrLRUStamp) {
191        // wrap around! just trash our LRU, one time hit.
192        for (int i = 0; i < fCount; ++i) {
193            fEntries[i]->fLRUStamp = 0;
194        }
195    }
196    ++fCurrLRUStamp;
197    return entry->fProgram;
198}
199
200////////////////////////////////////////////////////////////////////////////////
201
202void GrGpuGL::abandonResources(){
203    INHERITED::abandonResources();
204    fProgramCache->abandon();
205    fHWProgramID = 0;
206    fPathNameAllocator.reset(NULL);
207}
208
209////////////////////////////////////////////////////////////////////////////////
210
211#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
212
213bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstCopy) {
214    const GrDrawState& drawState = this->getDrawState();
215
216    // GrGpu::setupClipAndFlushState should have already checked this and bailed if not true.
217    SkASSERT(NULL != drawState.getRenderTarget());
218
219    if (kStencilPath_DrawType == type) {
220        const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
221        SkISize size;
222        size.set(rt->width(), rt->height());
223        this->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin());
224    } else {
225        this->flushMiscFixedFunctionState();
226
227        GrBlendCoeff srcCoeff;
228        GrBlendCoeff dstCoeff;
229        GrDrawState::BlendOptFlags blendOpts = drawState.getBlendOpts(false, &srcCoeff, &dstCoeff);
230        if (GrDrawState::kSkipDraw_BlendOptFlag & blendOpts) {
231            return false;
232        }
233
234        SkSTArray<8, const GrEffectStage*, true> colorStages;
235        SkSTArray<8, const GrEffectStage*, true> coverageStages;
236        GrGLProgramDesc desc;
237        GrGLProgramDesc::Build(this->getDrawState(),
238                               type,
239                               blendOpts,
240                               srcCoeff,
241                               dstCoeff,
242                               this,
243                               dstCopy,
244                               &colorStages,
245                               &coverageStages,
246                               &desc);
247
248        fCurrentProgram.reset(fProgramCache->getProgram(desc,
249                                                        colorStages.begin(),
250                                                        coverageStages.begin()));
251        if (NULL == fCurrentProgram.get()) {
252            SkDEBUGFAIL("Failed to create program!");
253            return false;
254        }
255
256        SkASSERT((kDrawPath_DrawType != type && kDrawPaths_DrawType != type)
257                 || !fCurrentProgram->hasVertexShader());
258
259        fCurrentProgram.get()->ref();
260
261        GrGLuint programID = fCurrentProgram->programID();
262        if (fHWProgramID != programID) {
263            GL_CALL(UseProgram(programID));
264            fHWProgramID = programID;
265        }
266
267        fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff);
268        this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
269
270        fCurrentProgram->setData(blendOpts,
271                                 colorStages.begin(),
272                                 coverageStages.begin(),
273                                 dstCopy,
274                                 &fSharedGLProgramState);
275    }
276    this->flushStencil(type);
277    this->flushScissor();
278    this->flushAAState(type);
279
280    SkIRect* devRect = NULL;
281    SkIRect devClipBounds;
282    if (drawState.isClipState()) {
283        this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds);
284        devRect = &devClipBounds;
285    }
286    // This must come after textures are flushed because a texture may need
287    // to be msaa-resolved (which will modify bound FBO state).
288    this->flushRenderTarget(devRect);
289
290    return true;
291}
292
293void GrGpuGL::setupGeometry(const DrawInfo& info, size_t* indexOffsetInBytes) {
294
295    GrGLsizei stride = static_cast<GrGLsizei>(this->getDrawState().getVertexSize());
296
297    size_t vertexOffsetInBytes = stride * info.startVertex();
298
299    const GeometryPoolState& geoPoolState = this->getGeomPoolState();
300
301    GrGLVertexBuffer* vbuf;
302    switch (this->getGeomSrc().fVertexSrc) {
303        case kBuffer_GeometrySrcType:
304            vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
305            break;
306        case kArray_GeometrySrcType:
307        case kReserved_GeometrySrcType:
308            this->finalizeReservedVertices();
309            vertexOffsetInBytes += geoPoolState.fPoolStartVertex * this->getGeomSrc().fVertexSize;
310            vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
311            break;
312        default:
313            vbuf = NULL; // suppress warning
314            SkFAIL("Unknown geometry src type!");
315    }
316
317    SkASSERT(NULL != vbuf);
318    SkASSERT(!vbuf->isMapped());
319    vertexOffsetInBytes += vbuf->baseOffset();
320
321    GrGLIndexBuffer* ibuf = NULL;
322    if (info.isIndexed()) {
323        SkASSERT(NULL != indexOffsetInBytes);
324
325        switch (this->getGeomSrc().fIndexSrc) {
326        case kBuffer_GeometrySrcType:
327            *indexOffsetInBytes = 0;
328            ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
329            break;
330        case kArray_GeometrySrcType:
331        case kReserved_GeometrySrcType:
332            this->finalizeReservedIndices();
333            *indexOffsetInBytes = geoPoolState.fPoolStartIndex * sizeof(GrGLushort);
334            ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
335            break;
336        default:
337            ibuf = NULL; // suppress warning
338            SkFAIL("Unknown geometry src type!");
339        }
340
341        SkASSERT(NULL != ibuf);
342        SkASSERT(!ibuf->isMapped());
343        *indexOffsetInBytes += ibuf->baseOffset();
344    }
345    GrGLAttribArrayState* attribState =
346        fHWGeometryState.bindArrayAndBuffersToDraw(this, vbuf, ibuf);
347
348    if (fCurrentProgram->hasVertexShader()) {
349        int vertexAttribCount = this->getDrawState().getVertexAttribCount();
350        uint32_t usedAttribArraysMask = 0;
351        const GrVertexAttrib* vertexAttrib = this->getDrawState().getVertexAttribs();
352
353        for (int vertexAttribIndex = 0; vertexAttribIndex < vertexAttribCount;
354             ++vertexAttribIndex, ++vertexAttrib) {
355
356            usedAttribArraysMask |= (1 << vertexAttribIndex);
357            GrVertexAttribType attribType = vertexAttrib->fType;
358            attribState->set(this,
359                             vertexAttribIndex,
360                             vbuf,
361                             GrGLAttribTypeToLayout(attribType).fCount,
362                             GrGLAttribTypeToLayout(attribType).fType,
363                             GrGLAttribTypeToLayout(attribType).fNormalized,
364                             stride,
365                             reinterpret_cast<GrGLvoid*>(
366                                 vertexOffsetInBytes + vertexAttrib->fOffset));
367        }
368        attribState->disableUnusedArrays(this, usedAttribArraysMask);
369    }
370}
371