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
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 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
207bool 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
294void 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