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