C++程序  |  846行  |  32.22 KB

/*
    Copyright 2011 Google Inc.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

#include "GrBinHashKey.h"
#include "GrGLProgram.h"
#include "GrGpuGLShaders.h"
#include "GrGpuVertex.h"
#include "GrMemory.h"
#include "GrNoncopyable.h"
#include "GrStringBuilder.h"
#include "GrRandom.h"

#define SKIP_CACHE_CHECK    true
#define GR_UINT32_MAX   static_cast<uint32_t>(-1)

#include "GrTHashCache.h"

class GrGpuGLShaders::ProgramCache : public ::GrNoncopyable {
private:
    class Entry;

#if GR_DEBUG
    typedef GrBinHashKey<Entry, 4> ProgramHashKey; // Flex the dynamic allocation muscle in debug
#else
    typedef GrBinHashKey<Entry, 64> ProgramHashKey;
#endif

    class Entry : public ::GrNoncopyable {
    public:
        Entry() {}
        void copyAndTakeOwnership(Entry& entry) {
            fProgramData.copyAndTakeOwnership(entry.fProgramData);
            fKey.copyAndTakeOwnership(entry.fKey); // ownership transfer
            fLRUStamp = entry.fLRUStamp;
        }

    public:
        int compare(const ProgramHashKey& key) const { return fKey.compare(key); }

    public:
        GrGLProgram::CachedData fProgramData;
        ProgramHashKey          fKey;
        unsigned int            fLRUStamp;
    };

    GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;

    // We may have kMaxEntries+1 shaders in the GL context because
    // we create a new shader before evicting from the cache.
    enum {
        kMaxEntries = 32
    };
    Entry        fEntries[kMaxEntries];
    int          fCount;
    unsigned int fCurrLRUStamp;

public:
    ProgramCache() 
        : fCount(0)
        , fCurrLRUStamp(0) {
    }

    ~ProgramCache() {
        for (int i = 0; i < fCount; ++i) {
            GrGpuGLShaders::DeleteProgram(&fEntries[i].fProgramData);
        }
    }

    void abandon() {
        fCount = 0;
    }

    void invalidateViewMatrices() {
        for (int i = 0; i < fCount; ++i) {
            // set to illegal matrix
            fEntries[i].fProgramData.fViewMatrix = GrMatrix::InvalidMatrix();
        }
    }

    GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc) {
        Entry newEntry;
        while (newEntry.fKey.doPass()) {
            desc.buildKey(newEntry.fKey);
        }
        Entry* entry = fHashCache.find(newEntry.fKey);
        if (NULL == entry) {
            if (!desc.genProgram(&newEntry.fProgramData)) {
                return NULL;
            }
            if (fCount < kMaxEntries) {
                entry = fEntries + fCount;
                ++fCount;
            } else {
                GrAssert(kMaxEntries == fCount);
                entry = fEntries;
                for (int i = 1; i < kMaxEntries; ++i) {
                    if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
                        entry = fEntries + i;
                    }
                }
                fHashCache.remove(entry->fKey, entry);
                GrGpuGLShaders::DeleteProgram(&entry->fProgramData);
            }
            entry->copyAndTakeOwnership(newEntry);
            fHashCache.insert(entry->fKey, entry);
        }

        entry->fLRUStamp = fCurrLRUStamp;
        if (GR_UINT32_MAX == fCurrLRUStamp) {
            // wrap around! just trash our LRU, one time hit.
            for (int i = 0; i < fCount; ++i) {
                fEntries[i].fLRUStamp = 0;
            }
        }
        ++fCurrLRUStamp;
        return &entry->fProgramData;
    }
};

void GrGpuGLShaders::abandonResources(){
    INHERITED::abandonResources();
    fProgramCache->abandon();
}

void GrGpuGLShaders::DeleteProgram(GrGLProgram::CachedData* programData) {
    GR_GL(DeleteShader(programData->fVShaderID));
    GR_GL(DeleteShader(programData->fFShaderID));
    GR_GL(DeleteProgram(programData->fProgramID));
    GR_DEBUGCODE(memset(programData, 0, sizeof(*programData));)
}

void GrGpuGLShaders::ProgramUnitTest() {

    static const int STAGE_OPTS[] = {
        0,
        GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit,
        GrGLProgram::ProgramDesc::StageDesc::kIdentity_CoordMapping
    };
    static const GrGLProgram::ProgramDesc::StageDesc::Modulation STAGE_MODULATES[] = {
        GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation,
        GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation
    };
    static const GrGLProgram::ProgramDesc::StageDesc::CoordMapping STAGE_COORD_MAPPINGS[] = {
        GrGLProgram::ProgramDesc::StageDesc::kIdentity_CoordMapping,
        GrGLProgram::ProgramDesc::StageDesc::kRadialGradient_CoordMapping,
        GrGLProgram::ProgramDesc::StageDesc::kSweepGradient_CoordMapping,
        GrGLProgram::ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping
    };
    static const GrGLProgram::ProgramDesc::StageDesc::FetchMode FETCH_MODES[] = {
        GrGLProgram::ProgramDesc::StageDesc::kSingle_FetchMode,
        GrGLProgram::ProgramDesc::StageDesc::k2x2_FetchMode,
    };
    GrGLProgram program;
    GrGLProgram::ProgramDesc& pdesc = program.fProgramDesc;

    static const int NUM_TESTS = 512;

    // GrRandoms nextU() values have patterns in the low bits
    // So using nextU() % array_count might never take some values.
    GrRandom random;
    for (int t = 0; t < NUM_TESTS; ++t) {

#if 0
        GrPrintf("\nTest Program %d\n-------------\n", t);
        static const int stop = -1;
        if (t == stop) {
            int breakpointhere = 9;
        }
#endif

        pdesc.fVertexLayout = 0;
        pdesc.fEmitsPointSize = random.nextF() > .5f;
        float colorType = random.nextF();
        if (colorType < 1.f / 3.f) {
            pdesc.fColorType = GrGLProgram::ProgramDesc::kAttribute_ColorType;
        } else if (colorType < 2.f / 3.f) {
            pdesc.fColorType = GrGLProgram::ProgramDesc::kUniform_ColorType;
        } else {
            pdesc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
        }

        int idx = (int)(random.nextF() * (SkXfermode::kCoeffModesCnt));
        pdesc.fColorFilterXfermode = (SkXfermode::Mode)idx;

        idx = (int)(random.nextF() * (kNumStages+1));
        pdesc.fFirstCoverageStage = idx;

        pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));

        if (fDualSourceBlendingSupport) {
            pdesc.fDualSrcOutput =
               (GrGLProgram::ProgramDesc::DualSrcOutput)
               (int)(random.nextF() * GrGLProgram::ProgramDesc::kDualSrcOutputCnt);
        } else {
            pdesc.fDualSrcOutput =
                                GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
        }

        for (int s = 0; s < kNumStages; ++s) {
            // enable the stage?
            if (random.nextF() > .5f) {
                // use separate tex coords?
                if (random.nextF() > .5f) {
                    int t = (int)(random.nextF() * kMaxTexCoords);
                    pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
                } else {
                    pdesc.fVertexLayout |= StagePosAsTexCoordVertexLayoutBit(s);
                }
            }
            // use text-formatted verts?
            if (random.nextF() > .5f) {
                pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
            }
            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_OPTS));
            pdesc.fStages[s].fOptFlags = STAGE_OPTS[idx];
            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_MODULATES));
            pdesc.fStages[s].fModulation = STAGE_MODULATES[idx];
            idx = (int)(random.nextF() * GR_ARRAY_COUNT(STAGE_COORD_MAPPINGS));
            pdesc.fStages[s].fCoordMapping = STAGE_COORD_MAPPINGS[idx];
            idx = (int)(random.nextF() * GR_ARRAY_COUNT(FETCH_MODES));
            pdesc.fStages[s].fFetchMode = FETCH_MODES[idx];
            pdesc.fStages[s].setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
        }
        GrGLProgram::CachedData cachedData;
        program.genProgram(&cachedData);
        DeleteProgram(&cachedData);
        bool again = false;
        if (again) {
            program.genProgram(&cachedData);
            DeleteProgram(&cachedData);
        }
    }
}

GrGpuGLShaders::GrGpuGLShaders() {

    resetContext();
    int major, minor;
    gl_version(&major, &minor);

    f4X4DownsampleFilterSupport = true;
    if (GR_GL_SUPPORT_DESKTOP) {
        fDualSourceBlendingSupport =
            major > 3 ||(3 == major && 3 <= minor) ||
            has_gl_extension("GL_ARB_blend_func_extended");
    } else {
        fDualSourceBlendingSupport = false;
    }

    fProgramData = NULL;
    fProgramCache = new ProgramCache();

#if 0
    ProgramUnitTest();
#endif
}

GrGpuGLShaders::~GrGpuGLShaders() {
    delete fProgramCache;
}

const GrMatrix& GrGpuGLShaders::getHWSamplerMatrix(int stage) {
    GrAssert(fProgramData);

    if (GrGLProgram::kSetAsAttribute == 
        fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
        return fHWDrawState.fSamplerStates[stage].getMatrix();
    } else {
        return fProgramData->fTextureMatrices[stage];
    }
}

void GrGpuGLShaders::recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
    GrAssert(fProgramData);
    if (GrGLProgram::kSetAsAttribute == 
        fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
        fHWDrawState.fSamplerStates[stage].setMatrix(matrix);
    } else {
        fProgramData->fTextureMatrices[stage] = matrix;
    }
}

void GrGpuGLShaders::resetContext() {
    INHERITED::resetContext();

    fHWGeometryState.fVertexLayout = 0;
    fHWGeometryState.fVertexOffset = ~0;
    GR_GL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
    for (int t = 0; t < kMaxTexCoords; ++t) {
        GR_GL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
    }
    GR_GL(EnableVertexAttribArray(GrGLProgram::PositionAttributeIdx()));

    fHWProgramID = 0;
}

void GrGpuGLShaders::flushViewMatrix() {
    GrAssert(NULL != fCurrDrawState.fRenderTarget);
    GrMatrix m;
    m.setAll(
        GrIntToScalar(2) / fCurrDrawState.fRenderTarget->width(), 0, -GR_Scalar1,
        0,-GrIntToScalar(2) / fCurrDrawState.fRenderTarget->height(), GR_Scalar1,
        0, 0, GrMatrix::I()[8]);
    m.setConcat(m, fCurrDrawState.fViewMatrix);

    // ES doesn't allow you to pass true to the transpose param,
    // so do our own transpose
    GrGLfloat mt[]  = {
        GrScalarToFloat(m[GrMatrix::kMScaleX]),
        GrScalarToFloat(m[GrMatrix::kMSkewY]),
        GrScalarToFloat(m[GrMatrix::kMPersp0]),
        GrScalarToFloat(m[GrMatrix::kMSkewX]),
        GrScalarToFloat(m[GrMatrix::kMScaleY]),
        GrScalarToFloat(m[GrMatrix::kMPersp1]),
        GrScalarToFloat(m[GrMatrix::kMTransX]),
        GrScalarToFloat(m[GrMatrix::kMTransY]),
        GrScalarToFloat(m[GrMatrix::kMPersp2])
    };

    if (GrGLProgram::kSetAsAttribute ==  
        fProgramData->fUniLocations.fViewMatrixUni) {
        int baseIdx = GrGLProgram::ViewMatrixAttributeIdx();
        GR_GL(VertexAttrib4fv(baseIdx + 0, mt+0));
        GR_GL(VertexAttrib4fv(baseIdx + 1, mt+3));
        GR_GL(VertexAttrib4fv(baseIdx + 2, mt+6));
    } else {
        GrAssert(GrGLProgram::kUnusedUniform != 
                 fProgramData->fUniLocations.fViewMatrixUni);
        GR_GL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,
                               1, false, mt));
    }
}

void GrGpuGLShaders::flushTextureDomain(int s) {
    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni;
    if (GrGLProgram::kUnusedUniform != uni) {
        const GrRect &texDom =
            fCurrDrawState.fSamplerStates[s].getTextureDomain();

        if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
            fProgramData->fTextureDomain[s] != texDom) {

            fProgramData->fTextureDomain[s] = texDom;

            float values[4] = {
                GrScalarToFloat(texDom.left()),
                GrScalarToFloat(texDom.top()),
                GrScalarToFloat(texDom.right()),
                GrScalarToFloat(texDom.bottom())
            };

            GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
            GrGLTexture::Orientation orientation = texture->orientation();

            // vertical flip if necessary
            if (GrGLTexture::kBottomUp_Orientation == orientation) {
                values[1] = 1.0f - values[1];
                values[3] = 1.0f - values[3];
                // The top and bottom were just flipped, so correct the ordering
                // of elements so that values = (l, t, r, b).
                SkTSwap(values[1], values[3]);
            }

            values[0] *= SkScalarToFloat(texture->contentScaleX());
            values[2] *= SkScalarToFloat(texture->contentScaleX());
            values[1] *= SkScalarToFloat(texture->contentScaleY());
            values[3] *= SkScalarToFloat(texture->contentScaleY());

            GR_GL(Uniform4fv(uni, 1, values));
        }
    }
}

void GrGpuGLShaders::flushTextureMatrix(int s) {
    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
    GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
    if (NULL != texture) {
        if (GrGLProgram::kUnusedUniform != uni &&
            (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
            getHWSamplerMatrix(s) != getSamplerMatrix(s))) {

            GrAssert(NULL != fCurrDrawState.fTextures[s]);

            GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];

            GrMatrix m = getSamplerMatrix(s);
            GrSamplerState::SampleMode mode = 
                fCurrDrawState.fSamplerStates[s].getSampleMode();
            AdjustTextureMatrix(texture, mode, &m);

            // ES doesn't allow you to pass true to the transpose param,
            // so do our own transpose
            GrGLfloat mt[]  = {
                GrScalarToFloat(m[GrMatrix::kMScaleX]),
                GrScalarToFloat(m[GrMatrix::kMSkewY]),
                GrScalarToFloat(m[GrMatrix::kMPersp0]),
                GrScalarToFloat(m[GrMatrix::kMSkewX]),
                GrScalarToFloat(m[GrMatrix::kMScaleY]),
                GrScalarToFloat(m[GrMatrix::kMPersp1]),
                GrScalarToFloat(m[GrMatrix::kMTransX]),
                GrScalarToFloat(m[GrMatrix::kMTransY]),
                GrScalarToFloat(m[GrMatrix::kMPersp2])
            };

            if (GrGLProgram::kSetAsAttribute ==
                fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) {
                int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s);
                GR_GL(VertexAttrib4fv(baseIdx + 0, mt+0));
                GR_GL(VertexAttrib4fv(baseIdx + 1, mt+3));
                GR_GL(VertexAttrib4fv(baseIdx + 2, mt+6));
            } else {
                GR_GL(UniformMatrix3fv(uni, 1, false, mt));
            }
            recordHWSamplerMatrix(s, getSamplerMatrix(s));
        }
    }
}

void GrGpuGLShaders::flushRadial2(int s) {

    const int &uni = fProgramData->fUniLocations.fStages[s].fRadial2Uni;
    const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
    if (GrGLProgram::kUnusedUniform != uni &&
        (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
         fProgramData->fRadial2Radius0[s]  != sampler.getRadial2Radius0()  ||
         fProgramData->fRadial2PosRoot[s]  != sampler.isRadial2PosRoot())) {

        GrScalar centerX1 = sampler.getRadial2CenterX1();
        GrScalar radius0 = sampler.getRadial2Radius0();

        GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;

        float values[6] = {
            GrScalarToFloat(a),
            1 / (2.f * values[0]),
            GrScalarToFloat(centerX1),
            GrScalarToFloat(radius0),
            GrScalarToFloat(GrMul(radius0, radius0)),
            sampler.isRadial2PosRoot() ? 1.f : -1.f
        };
        GR_GL(Uniform1fv(uni, 6, values));
        fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
        fProgramData->fRadial2Radius0[s]  = sampler.getRadial2Radius0();
        fProgramData->fRadial2PosRoot[s]  = sampler.isRadial2PosRoot();
    }
}

void GrGpuGLShaders::flushTexelSize(int s) {
    const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni;
    if (GrGLProgram::kUnusedUniform != uni) {
        GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
        if (texture->allocWidth() != fProgramData->fTextureWidth[s] ||
            texture->allocHeight() != fProgramData->fTextureWidth[s]) {

            float texelSize[] = {1.f / texture->allocWidth(),
                                 1.f / texture->allocHeight()};
            GR_GL(Uniform2fv(uni, 1, texelSize));
        }
    }
}

void GrGpuGLShaders::flushEdgeAAData() {
    const int& uni = fProgramData->fUniLocations.fEdgesUni;
    if (GrGLProgram::kUnusedUniform != uni) {
        int count = fCurrDrawState.fEdgeAANumEdges;
        Edge edges[kMaxEdges];
        // Flip the edges in Y
        float height = fCurrDrawState.fRenderTarget->height();
        for (int i = 0; i < count; ++i) {
            edges[i] = fCurrDrawState.fEdgeAAEdges[i];
            float b = edges[i].fY;
            edges[i].fY = -b;
            edges[i].fZ += b * height;
        }
        GR_GL(Uniform3fv(uni, count, &edges[0].fX));
    }
}

static const float ONE_OVER_255 = 1.f / 255.f;

#define GR_COLOR_TO_VEC4(color) {\
    GrColorUnpackR(color) * ONE_OVER_255,\
    GrColorUnpackG(color) * ONE_OVER_255,\
    GrColorUnpackB(color) * ONE_OVER_255,\
    GrColorUnpackA(color) * ONE_OVER_255 \
}

void GrGpuGLShaders::flushColor() {
    const GrGLProgram::ProgramDesc& desc = fCurrentProgram.getDesc();
    if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
        // color will be specified per-vertex as an attribute
        // invalidate the const vertex attrib color
        fHWDrawState.fColor = GrColor_ILLEGAL;
    } else {
        switch (desc.fColorType) {
            case GrGLProgram::ProgramDesc::kAttribute_ColorType:
                if (fHWDrawState.fColor != fCurrDrawState.fColor) {
                    // OpenGL ES only supports the float varities of glVertexAttrib
                    float c[] = GR_COLOR_TO_VEC4(fCurrDrawState.fColor);
                    GR_GL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(), c));
                    fHWDrawState.fColor = fCurrDrawState.fColor;
                }
                break;
            case GrGLProgram::ProgramDesc::kUniform_ColorType:
                if (fProgramData->fColor != fCurrDrawState.fColor) {
                    // OpenGL ES only supports the float varities of glVertexAttrib
                    float c[] = GR_COLOR_TO_VEC4(fCurrDrawState.fColor);
                    GrAssert(GrGLProgram::kUnusedUniform != 
                             fProgramData->fUniLocations.fColorUni);
                    GR_GL(Uniform4fv(fProgramData->fUniLocations.fColorUni, 1, c));
                    fProgramData->fColor = fCurrDrawState.fColor;
                }
                break;
            case GrGLProgram::ProgramDesc::kNone_ColorType:
                GrAssert(0xffffffff == fCurrDrawState.fColor);
                break;
            default:
                GrCrash("Unknown color type.");
        }
    }
    if (fProgramData->fUniLocations.fColorFilterUni
                != GrGLProgram::kUnusedUniform
            && fProgramData->fColorFilterColor
                != fCurrDrawState.fColorFilterColor) {
        float c[] = GR_COLOR_TO_VEC4(fCurrDrawState.fColorFilterColor);
        GR_GL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c));
        fProgramData->fColorFilterColor = fCurrDrawState.fColorFilterColor;
    }
}


bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
    if (!flushGLStateCommon(type)) {
        return false;
    }

    if (fDirtyFlags.fRenderTargetChanged) {
        // our coords are in pixel space and the GL matrices map to NDC
        // so if the viewport changed, our matrix is now wrong.
        fHWDrawState.fViewMatrix = GrMatrix::InvalidMatrix();
        // we assume all shader matrices may be wrong after viewport changes
        fProgramCache->invalidateViewMatrices();
    }

    buildProgram(type);
    fProgramData = fProgramCache->getProgramData(fCurrentProgram);
    if (NULL == fProgramData) {
        GrAssert(!"Failed to create program!");
        return false;
    }

    if (fHWProgramID != fProgramData->fProgramID) {
        GR_GL(UseProgram(fProgramData->fProgramID));
        fHWProgramID = fProgramData->fProgramID;
    }
    GrBlendCoeff srcCoeff = fCurrDrawState.fSrcBlend;
    GrBlendCoeff dstCoeff = fCurrDrawState.fDstBlend;
    
    fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
    this->flushBlend(type, srcCoeff, dstCoeff);

    this->flushColor();

    GrMatrix* currViewMatrix;
    if (GrGLProgram::kSetAsAttribute == 
        fProgramData->fUniLocations.fViewMatrixUni) {
        currViewMatrix = &fHWDrawState.fViewMatrix;
    } else {
        currViewMatrix = &fProgramData->fViewMatrix;
    }

    if (*currViewMatrix != fCurrDrawState.fViewMatrix) {
        flushViewMatrix();
        *currViewMatrix = fCurrDrawState.fViewMatrix;
    }

    for (int s = 0; s < kNumStages; ++s) {
        this->flushTextureMatrix(s);

        this->flushRadial2(s);

        this->flushTexelSize(s);

        this->flushTextureDomain(s);
    }
    this->flushEdgeAAData();
    resetDirtyFlags();
    return true;
}

void GrGpuGLShaders::postDraw() {
}

void GrGpuGLShaders::setupGeometry(int* startVertex,
                                    int* startIndex,
                                    int vertexCount,
                                    int indexCount) {

    int newColorOffset;
    int newTexCoordOffsets[kMaxTexCoords];

    GrGLsizei newStride = VertexSizeAndOffsetsByIdx(fGeometrySrc.fVertexLayout,
                                                  newTexCoordOffsets,
                                                  &newColorOffset);
    int oldColorOffset;
    int oldTexCoordOffsets[kMaxTexCoords];
    GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(fHWGeometryState.fVertexLayout,
                                                  oldTexCoordOffsets,
                                                  &oldColorOffset);
    bool indexed = NULL != startIndex;

    int extraVertexOffset;
    int extraIndexOffset;
    setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);

    GrGLenum scalarType;
    bool texCoordNorm;
    if (fGeometrySrc.fVertexLayout & kTextFormat_VertexLayoutBit) {
        scalarType = GrGLTextType;
        texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
    } else {
        scalarType = GrGLType;
        texCoordNorm = false;
    }

    size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
    *startVertex = 0;
    if (indexed) {
        *startIndex += extraIndexOffset;
    }

    // all the Pointers must be set if any of these are true
    bool allOffsetsChange =  fHWGeometryState.fArrayPtrsDirty ||
                             vertexOffset != fHWGeometryState.fVertexOffset ||
                             newStride != oldStride;

    // position and tex coord offsets change if above conditions are true
    // or the type/normalization changed based on text vs nontext type coords.
    bool posAndTexChange = allOffsetsChange ||
                           (((GrGLTextType != GrGLType) || GR_GL_TEXT_TEXTURE_NORMALIZED) &&
                                (kTextFormat_VertexLayoutBit &
                                  (fHWGeometryState.fVertexLayout ^
                                   fGeometrySrc.fVertexLayout)));

    if (posAndTexChange) {
        int idx = GrGLProgram::PositionAttributeIdx();
        GR_GL(VertexAttribPointer(idx, 2, scalarType, false, newStride, 
                                  (GrGLvoid*)vertexOffset));
        fHWGeometryState.fVertexOffset = vertexOffset;
    }

    for (int t = 0; t < kMaxTexCoords; ++t) {
        if (newTexCoordOffsets[t] > 0) {
            GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
            int idx = GrGLProgram::TexCoordAttributeIdx(t);
            if (oldTexCoordOffsets[t] <= 0) {
                GR_GL(EnableVertexAttribArray(idx));
                GR_GL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, 
                                          newStride, texCoordOffset));
            } else if (posAndTexChange ||
                       newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
                GR_GL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, 
                                          newStride, texCoordOffset));
            }
        } else if (oldTexCoordOffsets[t] > 0) {
            GR_GL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
        }
    }

    if (newColorOffset > 0) {
        GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
        int idx = GrGLProgram::ColorAttributeIdx();
        if (oldColorOffset <= 0) {
            GR_GL(EnableVertexAttribArray(idx));
            GR_GL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
                                      true, newStride, colorOffset));
        } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
            GR_GL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
                                      true, newStride, colorOffset));
        }
    } else if (oldColorOffset > 0) {
        GR_GL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
    }

    fHWGeometryState.fVertexLayout = fGeometrySrc.fVertexLayout;
    fHWGeometryState.fArrayPtrsDirty = false;
}

void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
    GrGLProgram::ProgramDesc& desc = fCurrentProgram.fProgramDesc;

    // Must initialize all fields or cache will have false negatives!
    desc.fVertexLayout = fGeometrySrc.fVertexLayout;

    desc.fEmitsPointSize = kPoints_PrimitiveType == type;

    bool requiresAttributeColors = desc.fVertexLayout & kColor_VertexLayoutBit;
    // fColorType records how colors are specified for the program. Strip
    // the bit from the layout to avoid false negatives when searching for an
    // existing program in the cache.
    desc.fVertexLayout &= ~(kColor_VertexLayoutBit);

    desc.fColorFilterXfermode = fCurrDrawState.fColorFilterXfermode;

#if GR_AGGRESSIVE_SHADER_OPTS
    if (!requiresAttributeColors && (0xffffffff == fCurrDrawState.fColor)) {
        desc.fColorType = GrGLProgram::ProgramDesc::kNone_ColorType;
    } else
#endif
#if GR_GL_NO_CONSTANT_ATTRIBUTES
    if (!requiresAttributeColors) {
        desc.fColorType = GrGLProgram::ProgramDesc::kUniform_ColorType;
    } else
#endif
    {
        if (requiresAttributeColors) {} // suppress unused var warning
        desc.fColorType = GrGLProgram::ProgramDesc::kAttribute_ColorType;
    }

    desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;

    int lastEnabledStage = -1;

    for (int s = 0; s < kNumStages; ++s) {
        GrGLProgram::ProgramDesc::StageDesc& stage = desc.fStages[s];

        stage.fOptFlags = 0;
        stage.setEnabled(this->isStageEnabled(s));

        if (stage.isEnabled()) {
            lastEnabledStage = s;
            GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
            GrAssert(NULL != texture);
            // we matrix to invert when orientation is TopDown, so make sure
            // we aren't in that case before flagging as identity.
            if (TextureMatrixIsIdentity(texture, fCurrDrawState.fSamplerStates[s])) {
                stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kIdentityMatrix_OptFlagBit;
            } else if (!getSamplerMatrix(s).hasPerspective()) {
                stage.fOptFlags |= GrGLProgram::ProgramDesc::StageDesc::kNoPerspective_OptFlagBit;
            }
            switch (fCurrDrawState.fSamplerStates[s].getSampleMode()) {
                case GrSamplerState::kNormal_SampleMode:
                    stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kIdentity_CoordMapping;
                    break;
                case GrSamplerState::kRadial_SampleMode:
                    stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadialGradient_CoordMapping;
                    break;
                case GrSamplerState::kRadial2_SampleMode:
                    stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kRadial2Gradient_CoordMapping;
                    break;
                case GrSamplerState::kSweep_SampleMode:
                    stage.fCoordMapping = GrGLProgram::ProgramDesc::StageDesc::kSweepGradient_CoordMapping;
                    break;
                default:
                    GrCrash("Unexpected sample mode!");
                    break;
            }

            switch (fCurrDrawState.fSamplerStates[s].getFilter()) {
                // these both can use a regular texture2D()
                case GrSamplerState::kNearest_Filter:
                case GrSamplerState::kBilinear_Filter:
                    stage.fFetchMode = GrGLProgram::ProgramDesc::StageDesc::kSingle_FetchMode;
                    break;
                // performs 4 texture2D()s
                case GrSamplerState::k4x4Downsample_Filter:
                    stage.fFetchMode = GrGLProgram::ProgramDesc::StageDesc::k2x2_FetchMode;
                    break;
                default:
                    GrCrash("Unexpected filter!");
                    break;
            }

            if (fCurrDrawState.fSamplerStates[s].hasTextureDomain()) {
                GrAssert(GrSamplerState::kClamp_WrapMode ==
                    fCurrDrawState.fSamplerStates[s].getWrapX() &&
                    GrSamplerState::kClamp_WrapMode ==
                    fCurrDrawState.fSamplerStates[s].getWrapY());
                stage.fOptFlags |=
                    GrGLProgram::ProgramDesc::StageDesc::
                    kCustomTextureDomain_OptFlagBit;
            }

            if (GrPixelConfigIsAlphaOnly(texture->config())) {
                stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kAlpha_Modulation;
            } else {
                stage.fModulation = GrGLProgram::ProgramDesc::StageDesc::kColor_Modulation;
            }
        } else {
            stage.fOptFlags     = 0;
            stage.fCoordMapping = (GrGLProgram::ProgramDesc::StageDesc::CoordMapping)0;
            stage.fModulation   = (GrGLProgram::ProgramDesc::StageDesc::Modulation)0;
        }
    }

    desc.fDualSrcOutput = GrGLProgram::ProgramDesc::kNone_DualSrcOutput;
    // use canonical value when coverage/color distinction won't affect
    // generated code to prevent duplicate programs.
    desc.fFirstCoverageStage = kNumStages;
    if (fCurrDrawState.fFirstCoverageStage <= lastEnabledStage) {
        // color filter is applied between color/coverage computation
        if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
            desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
        }

        // We could consider cases where the final color is solid (0xff alpha)
        // and the dst coeff can correctly be set to a non-dualsrc gl value.
        // (e.g. solid draw, and dst coeff is kZero. It's correct to make
        // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
        // kOne).
        if (fDualSourceBlendingSupport) {
            if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
                // write the coverage value to second color
                desc.fDualSrcOutput = 
                                GrGLProgram::ProgramDesc::kCoverage_DualSrcOutput;
                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
            } else if (kSA_BlendCoeff == fCurrDrawState.fDstBlend) {
                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
                // cover
                desc.fDualSrcOutput = 
                            GrGLProgram::ProgramDesc::kCoverageISA_DualSrcOutput;
                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
            } else if (kSC_BlendCoeff == fCurrDrawState.fDstBlend) {
                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
                // cover
                desc.fDualSrcOutput = 
                        GrGLProgram::ProgramDesc::kCoverageISC_DualSrcOutput;
                desc.fFirstCoverageStage = fCurrDrawState.fFirstCoverageStage;
            }
        }
    }
}