Exemple #1
0
void GrGLFullShaderBuilder::bindProgramLocations(GrGLuint programId) {
    this->INHERITED::bindProgramLocations(programId);

    const GrGLProgramDesc::KeyHeader& header = this->desc().getHeader();

    // Bind the attrib locations to same values for all shaders
    SkASSERT(-1 != header.fPositionAttributeIndex);
    GL_CALL(BindAttribLocation(programId,
                               header.fPositionAttributeIndex,
                               fPositionVar->c_str()));
    if (-1 != header.fLocalCoordAttributeIndex) {
        GL_CALL(BindAttribLocation(programId,
                                   header.fLocalCoordAttributeIndex,
                                   fLocalCoordsVar->c_str()));
    }
    if (-1 != header.fColorAttributeIndex) {
        GL_CALL(BindAttribLocation(programId,
                                   header.fColorAttributeIndex,
                                   color_attribute_name()));
    }
    if (-1 != header.fCoverageAttributeIndex) {
        GL_CALL(BindAttribLocation(programId,
                                   header.fCoverageAttributeIndex,
                                   coverage_attribute_name()));
    }

    const AttributePair* attribEnd = fEffectAttributes.end();
    for (const AttributePair* attrib = fEffectAttributes.begin(); attrib != attribEnd; ++attrib) {
         GL_CALL(BindAttribLocation(programId, attrib->fIndex, attrib->fName.c_str()));
    }
}
void GrGLVertexShaderBuilder::bindProgramLocations(GrGLuint programId) {
    const GrGLProgramDesc::KeyHeader& header = fProgramBuilder->desc().getHeader();
    GrGpuGL* gpu = fProgramBuilder->gpu();

    // Bind the attrib locations to same values for all shaders
    SkASSERT(-1 != header.fPositionAttributeIndex);
    GL_CALL(BindAttribLocation(programId,
                               header.fPositionAttributeIndex,
                               fPositionVar->c_str()));
    if (-1 != header.fLocalCoordAttributeIndex) {
        GL_CALL(BindAttribLocation(programId,
                                   header.fLocalCoordAttributeIndex,
                                   fLocalCoordsVar->c_str()));
    }
    if (-1 != header.fColorAttributeIndex) {
        GL_CALL(BindAttribLocation(programId,
                                   header.fColorAttributeIndex,
                                   color_attribute_name()));
    }
    if (-1 != header.fCoverageAttributeIndex) {
        GL_CALL(BindAttribLocation(programId,
                                   header.fCoverageAttributeIndex,
                                   coverage_attribute_name()));
    }

    // We pull the current state of attributes off of drawstate's optimized state and bind them in
    // order. This assumes that the drawState has not changed since we called flushGraphicsState()
    // higher up in the stack.
    const GrDrawTargetCaps* caps = fProgramBuilder->gpu()->caps();
    const GrDrawState& drawState = *fProgramBuilder->gpu()->drawState();
    SkAutoTUnref<GrOptDrawState> optState(drawState.createOptState(*caps));
    const GrVertexAttrib* vaPtr = optState->getVertexAttribs();
    const int vaCount = optState->getVertexAttribCount();

    int i = fEffectAttribOffset;
    for (int index = 0; index < vaCount; index++) {
        if (kGeometryProcessor_GrVertexAttribBinding != vaPtr[index].fBinding) {
            continue;
        }
        SkASSERT(index != header.fPositionAttributeIndex &&
                 index != header.fLocalCoordAttributeIndex &&
                 index != header.fColorAttributeIndex &&
                 index != header.fCoverageAttributeIndex);
        // We should never find another effect attribute if we have bound everything
        SkASSERT(i < fInputs.count());
        GL_CALL(BindAttribLocation(programId, index, fInputs[i].c_str()));
        i++;
    }
    // Make sure we bound everything
    SkASSERT(fInputs.count() == i);
}
bool NullTechnique::Init()
{
    if (!Technique::Init()) {
        return false;
    }

    if (!AddShader(GL_VERTEX_SHADER, "shaders/null_technique.vs")) {
        return false;
    }

    if (!AddShader(GL_FRAGMENT_SHADER, "shaders/null_technique.fs")) {
        return false;
    }
    BindAttribLocation(0, "Position");
    if (!Finalize()) {
        return false;
    }

    m_WVPLocation = GetUniformLocation("gWVP");

	if (m_WVPLocation == INVALID_UNIFORM_LOCATION) {
		return false;
	}

	return true;
}
void GrGLVertexBuilder::bindVertexAttributes(GrGLuint programID) {
    const GrPrimitiveProcessor& primProc = fProgramBuilder->primitiveProcessor();

    int vaCount = primProc.numAttribs();
    for (int i = 0; i < vaCount; i++) {
        GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName));
    }
    return;
}
void GrGLVertexBuilder::bindVertexAttributes(GrGLuint programID) {
    const GrGeometryProcessor* gp = fProgramBuilder->fOptState.getGeometryProcessor();

    const GrGeometryProcessor::VertexAttribArray& v = gp->getAttribs();
    int vaCount = v.count();
    for (int i = 0; i < vaCount; i++) {
        GL_CALL(BindAttribLocation(programID, i, v[i].fName));
    }
    return;
}
bool LightingTechnique::Init()
{
    if (!Technique::Init()) {
        return false;
    }

    if (!AddShader(GL_VERTEX_SHADER, "lighting.vs")) {
        return false;
    }

    if (!AddShader(GL_FRAGMENT_SHADER, "lighting.fs")) {
        return false;
    }

    if (!Finalize()) {
        return false;
    }
    BindAttribLocation(0, "Position");
    BindAttribLocation(1, "TexCoord");
    BindAttribLocation(2, "Normal");

    m_WVPLocation = GetUniformLocation("gWVP");
    m_WorldMatrixLocation = GetUniformLocation("gWorld");
    m_samplerLocation = GetUniformLocation("gSampler");
    m_dirLightLocation.Color = GetUniformLocation("gDirectionalLight.Color");
    m_dirLightLocation.AmbientIntensity = GetUniformLocation("gDirectionalLight.AmbientIntensity");
    m_dirLightLocation.Direction = GetUniformLocation("gDirectionalLight.Direction");
    m_dirLightLocation.DiffuseIntensity = GetUniformLocation("gDirectionalLight.DiffuseIntensity");

    if (m_dirLightLocation.AmbientIntensity == 0xFFFFFFFF ||
        m_WVPLocation == 0xFFFFFFFF ||
        m_WorldMatrixLocation == 0xFFFFFFFF ||
        m_samplerLocation == 0xFFFFFFFF ||
        m_dirLightLocation.Color == 0xFFFFFFFF ||
        m_dirLightLocation.DiffuseIntensity == 0xFFFFFFFF ||
        m_dirLightLocation.Direction == 0xFFFFFFFF) {
        return false;
    }

    return true;
}
GrGLProgram* GrGLProgramBuilder::finalize() {
    // verify we can get a program id
    GrGLuint programID;
    GL_CALL_RET(programID, CreateProgram());
    if (0 == programID) {
        this->cleanupFragmentProcessors();
        return nullptr;
    }

    // compile shaders and bind attributes / uniforms
    SkTDArray<GrGLuint> shadersToDelete;
    fVS.finalize(GrGLSLUniformHandler::kVertex_Visibility);
    if (!this->compileAndAttachShaders(fVS, programID, GR_GL_VERTEX_SHADER, &shadersToDelete)) {
        this->cleanupProgram(programID, shadersToDelete);
        return nullptr;
    }

    // NVPR actually requires a vertex shader to compile
    bool useNvpr = primitiveProcessor().isPathRendering();
    if (!useNvpr) {
        const GrPrimitiveProcessor& primProc = this->primitiveProcessor();

        int vaCount = primProc.numAttribs();
        for (int i = 0; i < vaCount; i++) {
            GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName));
        }
    }

    fFS.finalize(GrGLSLUniformHandler::kFragment_Visibility);
    if (!this->compileAndAttachShaders(fFS, programID, GR_GL_FRAGMENT_SHADER, &shadersToDelete)) {
        this->cleanupProgram(programID, shadersToDelete);
        return nullptr;
    }

    this->bindProgramResourceLocations(programID);

    GL_CALL(LinkProgram(programID));

    // Calling GetProgramiv is expensive in Chromium. Assume success in release builds.
    bool checkLinked = kChromium_GrGLDriver != fGpu->ctxInfo().driver();
#ifdef SK_DEBUG
    checkLinked = true;
#endif
    if (checkLinked) {
        checkLinkStatus(programID);
    }
    this->resolveProgramResourceLocations(programID);

    this->cleanupShaders(shadersToDelete);

    return this->createProgram(programID);
}
Exemple #8
0
GrGLProgram* GrGLProgramBuilder::finalize() {
    TRACE_EVENT0("skia", TRACE_FUNC);

    // verify we can get a program id
    GrGLuint programID;
    GL_CALL_RET(programID, CreateProgram());
    if (0 == programID) {
        this->cleanupFragmentProcessors();
        return nullptr;
    }

    if (this->gpu()->glCaps().programBinarySupport() &&
        this->gpu()->getContext()->contextPriv().getPersistentCache()) {
        GL_CALL(ProgramParameteri(programID, GR_GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GR_GL_TRUE));
    }

    this->finalizeShaders();

    // compile shaders and bind attributes / uniforms
    const GrPrimitiveProcessor& primProc = this->primitiveProcessor();
    SkSL::Program::Settings settings;
    settings.fCaps = this->gpu()->glCaps().shaderCaps();
    settings.fFlipY = this->pipeline().proxy()->origin() != kTopLeft_GrSurfaceOrigin;
    settings.fSharpenTextures = this->gpu()->getContext()->contextPriv().sharpenMipmappedTextures();
    SkSL::Program::Inputs inputs;
    SkTDArray<GrGLuint> shadersToDelete;
    bool cached = fGpu->glCaps().programBinarySupport() && nullptr != fCached.get();
    if (cached) {
        this->bindProgramResourceLocations(programID);
        // cache hit, just hand the binary to GL
        const uint8_t* bytes = fCached->bytes();
        size_t offset = 0;
        memcpy(&inputs, bytes + offset, sizeof(inputs));
        offset += sizeof(inputs);
        int binaryFormat;
        memcpy(&binaryFormat, bytes + offset, sizeof(binaryFormat));
        offset += sizeof(binaryFormat);
        GrGLClearErr(this->gpu()->glInterface());
        GR_GL_CALL_NOERRCHECK(this->gpu()->glInterface(),
                              ProgramBinary(programID, binaryFormat, (void*) (bytes + offset),
                                            fCached->size() - offset));
        if (GR_GL_GET_ERROR(this->gpu()->glInterface()) == GR_GL_NO_ERROR) {
            if (inputs.fRTHeight) {
                this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
            }
            cached = this->checkLinkStatus(programID);
        } else {
            cached = false;
        }
    }
    if (!cached) {
        // cache miss, compile shaders
        if (fFS.fForceHighPrecision) {
            settings.fForceHighPrecision = true;
        }
        SkSL::String glsl;
        std::unique_ptr<SkSL::Program> fs = GrSkSLtoGLSL(gpu()->glContext(),
                                                         GR_GL_FRAGMENT_SHADER,
                                                         fFS.fCompilerStrings.begin(),
                                                         fFS.fCompilerStringLengths.begin(),
                                                         fFS.fCompilerStrings.count(),
                                                         settings,
                                                         &glsl);
        inputs = fs->fInputs;
        if (inputs.fRTHeight) {
            this->addRTHeightUniform(SKSL_RTHEIGHT_NAME);
        }
        if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID,
                                           GR_GL_FRAGMENT_SHADER, &shadersToDelete, settings,
                                           inputs)) {
            this->cleanupProgram(programID, shadersToDelete);
            return nullptr;
        }

        std::unique_ptr<SkSL::Program> vs = GrSkSLtoGLSL(gpu()->glContext(),
                                                         GR_GL_VERTEX_SHADER,
                                                         fVS.fCompilerStrings.begin(),
                                                         fVS.fCompilerStringLengths.begin(),
                                                         fVS.fCompilerStrings.count(),
                                                         settings,
                                                         &glsl);
        if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID,
                                           GR_GL_VERTEX_SHADER, &shadersToDelete, settings,
                                           inputs)) {
            this->cleanupProgram(programID, shadersToDelete);
            return nullptr;
        }

        // NVPR actually requires a vertex shader to compile
        bool useNvpr = primProc.isPathRendering();
        if (!useNvpr) {
            int vaCount = primProc.numAttribs();
            for (int i = 0; i < vaCount; i++) {
                GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName));
            }
        }

        if (primProc.willUseGeoShader()) {
            std::unique_ptr<SkSL::Program> gs;
            gs = GrSkSLtoGLSL(gpu()->glContext(),
                              GR_GL_GEOMETRY_SHADER,
                              fGS.fCompilerStrings.begin(),
                              fGS.fCompilerStringLengths.begin(),
                              fGS.fCompilerStrings.count(),
                              settings,
                              &glsl);
            if (!this->compileAndAttachShaders(glsl.c_str(), glsl.size(), programID,
                                               GR_GL_GEOMETRY_SHADER, &shadersToDelete, settings,
                                               inputs)) {
                this->cleanupProgram(programID, shadersToDelete);
                return nullptr;
            }

        }
        this->bindProgramResourceLocations(programID);

        GL_CALL(LinkProgram(programID));
    }
    // Calling GetProgramiv is expensive in Chromium. Assume success in release builds.
    bool checkLinked = kChromium_GrGLDriver != fGpu->ctxInfo().driver();
#ifdef SK_DEBUG
    checkLinked = true;
#endif
    if (checkLinked) {
        if (!this->checkLinkStatus(programID)) {
            SkDebugf("VS:\n");
            GrGLPrintShader(fGpu->glContext(), GR_GL_VERTEX_SHADER, fVS.fCompilerStrings.begin(),
                            fVS.fCompilerStringLengths.begin(), fVS.fCompilerStrings.count(),
                            settings);
            if (primProc.willUseGeoShader()) {
                SkDebugf("\nGS:\n");
                GrGLPrintShader(fGpu->glContext(), GR_GL_GEOMETRY_SHADER,
                                fGS.fCompilerStrings.begin(), fGS.fCompilerStringLengths.begin(),
                                fGS.fCompilerStrings.count(), settings);
            }
            SkDebugf("\nFS:\n");
            GrGLPrintShader(fGpu->glContext(), GR_GL_FRAGMENT_SHADER, fFS.fCompilerStrings.begin(),
                            fFS.fCompilerStringLengths.begin(), fFS.fCompilerStrings.count(),
                            settings);
            SkDEBUGFAIL("");
            return nullptr;
        }
    }
    this->resolveProgramResourceLocations(programID);

    this->cleanupShaders(shadersToDelete);
    if (!cached && this->gpu()->getContext()->contextPriv().getPersistentCache() &&
        fGpu->glCaps().programBinarySupport()) {
        GrGLsizei length = 0;
        GL_CALL(GetProgramiv(programID, GL_PROGRAM_BINARY_LENGTH, &length));
        if (length > 0) {
            // store shader in cache
            sk_sp<SkData> key = SkData::MakeWithoutCopy(desc()->asKey(), desc()->keyLength());
            GrGLenum binaryFormat;
            std::unique_ptr<char[]> binary(new char[length]);
            GL_CALL(GetProgramBinary(programID, length, &length, &binaryFormat, binary.get()));
            size_t dataLength = sizeof(inputs) + sizeof(binaryFormat) + length;
            std::unique_ptr<uint8_t[]> data(new uint8_t[dataLength]);
            size_t offset = 0;
            memcpy(data.get() + offset, &inputs, sizeof(inputs));
            offset += sizeof(inputs);
            memcpy(data.get() + offset, &binaryFormat, sizeof(binaryFormat));
            offset += sizeof(binaryFormat);
            memcpy(data.get() + offset, binary.get(), length);
            this->gpu()->getContext()->contextPriv().getPersistentCache()->store(
                                            *key, *SkData::MakeWithoutCopy(data.get(), dataLength));
        }
    }
    return this->createProgram(programID);
}
bool LightingTechnique::Init()
{
    if (!Technique::Init()) {
        return false;
    }

    if (!AddShader(GL_VERTEX_SHADER, "lighting.vs")) {
        return false;
    }

    if (!AddShader(GL_FRAGMENT_SHADER, "lighting.fs")) {
        return false;
    }

    BindAttribLocation(0, "Position");
    BindAttribLocation(1, "TexCoord");
    BindAttribLocation(2, "Normal");

    if (!Finalize()) {
        return false;
    }

    m_WVPLocation = GetUniformLocation("gWVP");
    m_LightWVPLocation = GetUniformLocation("gLightWVP");
    m_WorldMatrixLocation = GetUniformLocation("gWorld");
    m_samplerLocation = GetUniformLocation("gSampler");
    m_shadowMapLocation = GetUniformLocation("gShadowMap");
    m_eyeWorldPosLocation = GetUniformLocation("gEyeWorldPos");
    m_dirLightLocation.Color = GetUniformLocation("gDirectionalLight.Base.Color");
    m_dirLightLocation.AmbientIntensity = GetUniformLocation("gDirectionalLight.Base.AmbientIntensity");
    m_dirLightLocation.Direction = GetUniformLocation("gDirectionalLight.Direction");
    m_dirLightLocation.DiffuseIntensity = GetUniformLocation("gDirectionalLight.Base.DiffuseIntensity");
    m_matSpecularIntensityLocation = GetUniformLocation("gMatSpecularIntensity");
    m_matSpecularPowerLocation = GetUniformLocation("gSpecularPower");
    m_numPointLightsLocation = GetUniformLocation("gNumPointLights");
    m_numSpotLightsLocation = GetUniformLocation("gNumSpotLights");

    if (m_dirLightLocation.AmbientIntensity == INVALID_UNIFORM_LOCATION ||
        m_WVPLocation == INVALID_UNIFORM_LOCATION ||
        m_LightWVPLocation == INVALID_UNIFORM_LOCATION ||
        m_WorldMatrixLocation == INVALID_UNIFORM_LOCATION ||
        m_samplerLocation == INVALID_UNIFORM_LOCATION ||
        m_shadowMapLocation == INVALID_UNIFORM_LOCATION ||
        m_eyeWorldPosLocation == INVALID_UNIFORM_LOCATION ||
        m_dirLightLocation.Color == INVALID_UNIFORM_LOCATION ||
        m_dirLightLocation.DiffuseIntensity == INVALID_UNIFORM_LOCATION ||
        m_dirLightLocation.Direction == INVALID_UNIFORM_LOCATION ||
        m_matSpecularIntensityLocation == INVALID_UNIFORM_LOCATION ||
        m_matSpecularPowerLocation == INVALID_UNIFORM_LOCATION ||
        m_numPointLightsLocation == INVALID_UNIFORM_LOCATION ||
        m_numSpotLightsLocation == INVALID_UNIFORM_LOCATION) {
        return false;
    }

    for (unsigned int i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(m_pointLightsLocation) ; i++) {
        char Name[128];
        memset(Name, 0, sizeof(Name));
        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Base.Color", i);
        m_pointLightsLocation[i].Color = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Base.AmbientIntensity", i);
        m_pointLightsLocation[i].AmbientIntensity = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Position", i);
        m_pointLightsLocation[i].Position = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Base.DiffuseIntensity", i);
        m_pointLightsLocation[i].DiffuseIntensity = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Atten.Constant", i);
        m_pointLightsLocation[i].Atten.Constant = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Atten.Linear", i);
        m_pointLightsLocation[i].Atten.Linear = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gPointLights[%d].Atten.Exp", i);
        m_pointLightsLocation[i].Atten.Exp = GetUniformLocation(Name);

        if (m_pointLightsLocation[i].Color == INVALID_UNIFORM_LOCATION ||
            m_pointLightsLocation[i].AmbientIntensity == INVALID_UNIFORM_LOCATION ||
            m_pointLightsLocation[i].Position == INVALID_UNIFORM_LOCATION ||
            m_pointLightsLocation[i].DiffuseIntensity == INVALID_UNIFORM_LOCATION ||
            m_pointLightsLocation[i].Atten.Constant == INVALID_UNIFORM_LOCATION ||
            m_pointLightsLocation[i].Atten.Linear == INVALID_UNIFORM_LOCATION ||
            m_pointLightsLocation[i].Atten.Exp == INVALID_UNIFORM_LOCATION) {
            return false;
        }
    }

    for (unsigned int i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(m_spotLightsLocation) ; i++) {
        char Name[128];
        memset(Name, 0, sizeof(Name));
        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Base.Color", i);
        m_spotLightsLocation[i].Color = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Base.AmbientIntensity", i);
        m_spotLightsLocation[i].AmbientIntensity = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Position", i);
        m_spotLightsLocation[i].Position = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Direction", i);
        m_spotLightsLocation[i].Direction = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Cutoff", i);
        m_spotLightsLocation[i].Cutoff = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Base.DiffuseIntensity", i);
        m_spotLightsLocation[i].DiffuseIntensity = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Atten.Constant", i);
        m_spotLightsLocation[i].Atten.Constant = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Atten.Linear", i);
        m_spotLightsLocation[i].Atten.Linear = GetUniformLocation(Name);

        SNPRINTF(Name, sizeof(Name), "gSpotLights[%d].Base.Atten.Exp", i);
        m_spotLightsLocation[i].Atten.Exp = GetUniformLocation(Name);

        if (m_spotLightsLocation[i].Color == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].AmbientIntensity == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].Position == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].Direction == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].Cutoff == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].DiffuseIntensity == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].Atten.Constant == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].Atten.Linear == INVALID_UNIFORM_LOCATION ||
            m_spotLightsLocation[i].Atten.Exp == INVALID_UNIFORM_LOCATION) {
            return false;
        }
    }

    return true;
}
void GrGLProgram::genProgram(GrGLProgram::CachedData* programData, 
                             const GrDrawTarget* target) const {

    ShaderCodeSegments segments;
    const uint32_t& layout = fProgramDesc.fVertexLayout;

    memset(&programData->fUniLocations, 0, sizeof(UniLocations));

    bool haveColor = !(ProgramDesc::kVertexColorAllOnes_OptFlagBit &
                       fProgramDesc.fOptFlags);

#if ATTRIBUTE_MATRIX
    segments.fVSAttrs = "attribute mat3 " VIEW_MATRIX_NAME ";\n";
#else
    segments.fVSUnis  = "uniform mat3 " VIEW_MATRIX_NAME ";\n";
    segments.fVSAttrs = "";
#endif
    segments.fVSAttrs += "attribute vec2 " POS_ATTR_NAME ";\n";
    if (haveColor) {
        segments.fVSAttrs += "attribute vec4 " COL_ATTR_NAME ";\n";
        segments.fVaryings = "varying vec4 vColor;\n";
    } else {
        segments.fVaryings = "";
    }

    segments.fVSCode   = "void main() {\n"
                         "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3(" POS_ATTR_NAME ", 1);\n"
                         "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n";
    if (haveColor) {
        segments.fVSCode += "\tvColor = " COL_ATTR_NAME ";\n";
    }

    if (!(fProgramDesc.fOptFlags & ProgramDesc::kNotPoints_OptFlagBit)) {
        segments.fVSCode += "\tgl_PointSize = 1.0;\n";
    }
    segments.fFSCode   = "void main() {\n";

    // add texture coordinates that are used to the list of vertex attr decls
    GrTokenString texCoordAttrs[GrDrawTarget::kMaxTexCoords];
    for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
        if (target->VertexUsesTexCoordIdx(t, layout)) {
            tex_attr_name(t, texCoordAttrs + t);

            segments.fVSAttrs += "attribute vec2 ";
            segments.fVSAttrs += texCoordAttrs[t];
            segments.fVSAttrs += ";\n";
        }
    }

    // for each enabled stage figure out what the input coordinates are
    // and count the number of stages in use.
    const char* stageInCoords[GrDrawTarget::kNumStages];
    int numActiveStages = 0;

    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
        if (fProgramDesc.fStages[s].fEnabled) {
            if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) & layout) {
                stageInCoords[s] = POS_ATTR_NAME;
            } else {
                int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
                 // we better have input tex coordinates if stage is enabled.
                GrAssert(tcIdx >= 0);
                GrAssert(texCoordAttrs[tcIdx].length());
                stageInCoords[s] = texCoordAttrs[tcIdx].cstr();
            }
            ++numActiveStages;
        }
    }

    GrTokenString inColor = "vColor";

    // if we have active stages string them together, feeding the output color
    // of each to the next and generating code for each stage.
    if (numActiveStages) {
        int currActiveStage = 0;
        for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
            if (fProgramDesc.fStages[s].fEnabled) {
                GrTokenString outColor;
                if (currActiveStage < (numActiveStages - 1)) {
                    outColor = "color";
                    outColor.appendInt(currActiveStage);
                    segments.fFSCode += "\tvec4 ";
                    segments.fFSCode += outColor;
                    segments.fFSCode += ";\n";
                } else {
                    outColor = "gl_FragColor";
                }

                genStageCode(s,
                             fProgramDesc.fStages[s],
                             haveColor ? inColor.cstr() : NULL,
                             outColor.cstr(),
                             stageInCoords[s],
                             &segments,
                             &programData->fUniLocations.fStages[s]);
                ++currActiveStage;
                inColor = outColor;
                haveColor = true;
            }
        }
    } else {
        segments.fFSCode += "\tgl_FragColor = ";
        if (haveColor) {
            segments.fFSCode += inColor;
        } else {
            segments.fFSCode += "vec4(1,1,1,1)";
        }
        segments.fFSCode += ";\n";
    }
    segments.fFSCode += "}\n";
    segments.fVSCode += "}\n";


    const char* strings[4];
    int lengths[4];
    int stringCnt = 0;

    if (segments.fVSUnis.length()) {
        strings[stringCnt] = segments.fVSUnis.cstr();
        lengths[stringCnt] = segments.fVSUnis.length();
        ++stringCnt;
    }
    if (segments.fVSAttrs.length()) {
        strings[stringCnt] = segments.fVSAttrs.cstr();
        lengths[stringCnt] = segments.fVSAttrs.length();
        ++stringCnt;
    }
    if (segments.fVaryings.length()) {
        strings[stringCnt] = segments.fVaryings.cstr();
        lengths[stringCnt] = segments.fVaryings.length();
        ++stringCnt;
    }

    GrAssert(segments.fVSCode.length());
    strings[stringCnt] = segments.fVSCode.cstr();
    lengths[stringCnt] = segments.fVSCode.length();
    ++stringCnt;

#if PRINT_SHADERS
    GrPrintf("%s%s%s%s\n",
             segments.fVSUnis.cstr(),
             segments.fVSAttrs.cstr(),
             segments.fVaryings.cstr(),
             segments.fVSCode.cstr());
#endif
    programData->fVShaderID = CompileShader(GR_GL_VERTEX_SHADER,
                                        stringCnt,
                                        strings,
                                        lengths);

    stringCnt = 0;

    if (strlen(GrShaderPrecision()) > 1) {
        strings[stringCnt] = GrShaderPrecision();
        lengths[stringCnt] = strlen(GrShaderPrecision());
        ++stringCnt;
    }
    if (segments.fFSUnis.length()) {
        strings[stringCnt] = segments.fFSUnis.cstr();
        lengths[stringCnt] = segments.fFSUnis.length();
        ++stringCnt;
    }
    if (segments.fVaryings.length()) {
        strings[stringCnt] = segments.fVaryings.cstr();
        lengths[stringCnt] = segments.fVaryings.length();
        ++stringCnt;
    }

    GrAssert(segments.fFSCode.length());
    strings[stringCnt] = segments.fFSCode.cstr();
    lengths[stringCnt] = segments.fFSCode.length();
    ++stringCnt;

#if PRINT_SHADERS
    GrPrintf("%s%s%s%s\n",
             GR_SHADER_PRECISION,
             segments.fFSUnis.cstr(),
             segments.fVaryings.cstr(),
             segments.fFSCode.cstr());
#endif
    programData->fFShaderID = CompileShader(GR_GL_FRAGMENT_SHADER,
                                            stringCnt,
                                            strings,
                                            lengths);

    programData->fProgramID = GR_GL(CreateProgram());
    const GrGLint& progID = programData->fProgramID;

    GR_GL(AttachShader(progID, programData->fVShaderID));
    GR_GL(AttachShader(progID, programData->fFShaderID));

    // Bind the attrib locations to same values for all shaders
    GR_GL(BindAttribLocation(progID, POS_ATTR_LOCATION, POS_ATTR_NAME));
    for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
        if (texCoordAttrs[t].length()) {
            GR_GL(BindAttribLocation(progID,
                                     TEX_ATTR_LOCATION(t),
                                     texCoordAttrs[t].cstr()));
        }
    }

#if ATTRIBUTE_MATRIX
    // set unis to a bogus value so that checks against -1 before
    // flushing will pass.
    GR_GL(BindAttribLocation(progID,
                             VIEWMAT_ATTR_LOCATION,
                             VIEW_MATRIX_NAME));

    program->fUniLocations.fViewMatrixUni = BOGUS_MATRIX_UNI_LOCATION;

    for (int s = 0; s < kNumStages; ++s) {
        if (fProgramDesc.fStages[s].fEnabled) {
            GrStringBuilder matName;
            tex_matrix_name(s, &matName);
            GR_GL(BindAttribLocation(progID,
                                     TEXMAT_ATTR_LOCATION(s),
                                     matName.cstr()));
            program->fUniLocations.fStages[s].fTextureMatrixUni =
                                                    BOGUS_MATRIX_UNI_LOCATION;
        }
    }
#endif

    GR_GL(BindAttribLocation(progID, COL_ATTR_LOCATION, COL_ATTR_NAME));

    GR_GL(LinkProgram(progID));

    GrGLint linked = GR_GL_INIT_ZERO;
    GR_GL(GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
    if (!linked) {
        GrGLint infoLen = GR_GL_INIT_ZERO;
        GR_GL(GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
        GrAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
        if (infoLen > 0) {
            GR_GL(GetProgramInfoLog(progID,
                                    infoLen+1,
                                    NULL,
                                    (char*)log.get()));
            GrPrintf((char*)log.get());
        }
        GrAssert(!"Error linking program");
        GR_GL(DeleteProgram(progID));
        programData->fProgramID = 0;
        return;
    }

    // Get uniform locations
#if !ATTRIBUTE_MATRIX
    programData->fUniLocations.fViewMatrixUni =
                    GR_GL(GetUniformLocation(progID, VIEW_MATRIX_NAME));
    GrAssert(-1 != programData->fUniLocations.fViewMatrixUni);
#endif
    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
        StageUniLocations& locations = programData->fUniLocations.fStages[s];
        if (fProgramDesc.fStages[s].fEnabled) {
#if !ATTRIBUTE_MATRIX
            if (locations.fTextureMatrixUni) {
                GrTokenString texMName;
                tex_matrix_name(s, &texMName);
                locations.fTextureMatrixUni = GR_GL(GetUniformLocation(
                                                progID,
                                                texMName.cstr()));
                GrAssert(-1 != locations.fTextureMatrixUni);
            } else {
                locations.fTextureMatrixUni = -1;

            }
#endif

            if (locations.fSamplerUni) {
                GrTokenString samplerName;
                sampler_name(s, &samplerName);
                locations.fSamplerUni = GR_GL(GetUniformLocation(
                                                     progID,
                                                     samplerName.cstr()));
                GrAssert(-1 != locations.fSamplerUni);
            } else {
                locations.fSamplerUni = -1;
            }

            if (locations.fRadial2Uni) {
                GrTokenString radial2ParamName;
                radial2_param_name(s, &radial2ParamName);
                locations.fRadial2Uni = GR_GL(GetUniformLocation(
                                             progID,
                                             radial2ParamName.cstr()));
                GrAssert(-1 != locations.fRadial2Uni);
            } else {
                locations.fRadial2Uni = -1;
            }
        } else {
            locations.fSamplerUni = -1;
            locations.fRadial2Uni = -1;
            locations.fTextureMatrixUni = -1;
        }
    }
    GR_GL(UseProgram(progID));

    // init sampler unis and set bogus values for state tracking
    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
        if (-1 != programData->fUniLocations.fStages[s].fSamplerUni) {
            GR_GL(Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
        }
        programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
        programData->fRadial2CenterX1[s] = GR_ScalarMax;
        programData->fRadial2Radius0[s] = -GR_ScalarMax;
    }
    programData->fViewMatrix = GrMatrix::InvalidMatrix();
}