| Index: src/gpu/gl/GrGLProgram.cpp
|
| diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
|
| index ac9794d4d5d3f5994cfcb427ae3293c839ff5f3f..f10e8954f20045758ac77e9e75aaf85077fdcaea 100644
|
| --- a/src/gpu/gl/GrGLProgram.cpp
|
| +++ b/src/gpu/gl/GrGLProgram.cpp
|
| @@ -14,28 +14,13 @@
|
| #include "GrGpuGL.h"
|
| #include "GrGLShaderVar.h"
|
| #include "GrGLSL.h"
|
| -#include "SkTrace.h"
|
| #include "SkXfermode.h"
|
|
|
| -#include "SkRTConf.h"
|
| -
|
| SK_DEFINE_INST_COUNT(GrGLProgram)
|
|
|
| #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X)
|
| #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fGpu->glInterface(), R, X)
|
|
|
| -SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false,
|
| - "Print the source code for all shaders generated.");
|
| -
|
| -#define COL_ATTR_NAME "aColor"
|
| -#define COV_ATTR_NAME "aCoverage"
|
| -#define EDGE_ATTR_NAME "aEdge"
|
| -
|
| -namespace {
|
| -inline const char* declared_color_output_name() { return "fsColorOut"; }
|
| -inline const char* dual_source_output_name() { return "dualSourceOut"; }
|
| -}
|
| -
|
| GrGLProgram* GrGLProgram::Create(GrGpuGL* gpu,
|
| const GrGLProgramDesc& desc,
|
| const GrEffectStage* colorStages[],
|
| @@ -55,9 +40,6 @@ GrGLProgram::GrGLProgram(GrGpuGL* gpu,
|
| : fGpu(gpu)
|
| , fUniformManager(gpu) {
|
| fDesc = desc;
|
| - fVShaderID = 0;
|
| - fGShaderID = 0;
|
| - fFShaderID = 0;
|
| fProgramID = 0;
|
|
|
| fDstCopyTexUnit = -1;
|
| @@ -72,24 +54,12 @@ GrGLProgram::GrGLProgram(GrGpuGL* gpu,
|
| }
|
|
|
| GrGLProgram::~GrGLProgram() {
|
| - if (fVShaderID) {
|
| - GL_CALL(DeleteShader(fVShaderID));
|
| - }
|
| - if (fGShaderID) {
|
| - GL_CALL(DeleteShader(fGShaderID));
|
| - }
|
| - if (fFShaderID) {
|
| - GL_CALL(DeleteShader(fFShaderID));
|
| - }
|
| if (fProgramID) {
|
| GL_CALL(DeleteProgram(fProgramID));
|
| }
|
| }
|
|
|
| void GrGLProgram::abandon() {
|
| - fVShaderID = 0;
|
| - fGShaderID = 0;
|
| - fFShaderID = 0;
|
| fProgramID = 0;
|
| }
|
|
|
| @@ -219,167 +189,7 @@ void add_color_filter(GrGLShaderBuilder* builder,
|
| }
|
| }
|
|
|
| -GrSLConstantVec GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
|
| - switch (fDesc.getHeader().fColorInput) {
|
| - case GrGLProgramDesc::kAttribute_ColorInput: {
|
| - GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
|
| - SkASSERT(NULL != vertexBuilder);
|
| - vertexBuilder->addAttribute(kVec4f_GrSLType, COL_ATTR_NAME);
|
| - const char *vsName, *fsName;
|
| - vertexBuilder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
|
| - vertexBuilder->vsCodeAppendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
|
| - *inColor = fsName;
|
| - return kNone_GrSLConstantVec;
|
| - }
|
| - case GrGLProgramDesc::kUniform_ColorInput: {
|
| - const char* name;
|
| - fUniformHandles.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
|
| - kVec4f_GrSLType, "Color", &name);
|
| - *inColor = name;
|
| - return kNone_GrSLConstantVec;
|
| - }
|
| - case GrGLProgramDesc::kTransBlack_ColorInput:
|
| - inColor->reset();
|
| - return kZeros_GrSLConstantVec;
|
| - case GrGLProgramDesc::kSolidWhite_ColorInput:
|
| - inColor->reset();
|
| - return kOnes_GrSLConstantVec;
|
| - default:
|
| - GrCrash("Unknown color type.");
|
| - return kNone_GrSLConstantVec;
|
| - }
|
| -}
|
| -
|
| -GrSLConstantVec GrGLProgram::genInputCoverage(GrGLShaderBuilder* builder, SkString* inCoverage) {
|
| - switch (fDesc.getHeader().fCoverageInput) {
|
| - case GrGLProgramDesc::kAttribute_ColorInput: {
|
| - GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder->getVertexBuilder();
|
| - SkASSERT(NULL != vertexBuilder);
|
| - vertexBuilder->addAttribute(kVec4f_GrSLType, COV_ATTR_NAME);
|
| - const char *vsName, *fsName;
|
| - vertexBuilder->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
|
| - vertexBuilder->vsCodeAppendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
|
| - *inCoverage = fsName;
|
| - return kNone_GrSLConstantVec;
|
| - }
|
| - case GrGLProgramDesc::kUniform_ColorInput: {
|
| - const char* name;
|
| - fUniformHandles.fCoverageUni =
|
| - builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
|
| - kVec4f_GrSLType, "Coverage", &name);
|
| - *inCoverage = name;
|
| - return kNone_GrSLConstantVec;
|
| - }
|
| - case GrGLProgramDesc::kTransBlack_ColorInput:
|
| - inCoverage->reset();
|
| - return kZeros_GrSLConstantVec;
|
| - case GrGLProgramDesc::kSolidWhite_ColorInput:
|
| - inCoverage->reset();
|
| - return kOnes_GrSLConstantVec;
|
| - default:
|
| - GrCrash("Unknown color type.");
|
| - return kNone_GrSLConstantVec;
|
| - }
|
| -}
|
| -
|
| -void GrGLProgram::genGeometryShader(GrGLShaderBuilder::VertexBuilder* vertexBuilder) const {
|
| -#if GR_GL_EXPERIMENTAL_GS
|
| - // TODO: The builder should add all this glue code.
|
| - if (fDesc.getHeader().fExperimentalGS) {
|
| - SkASSERT(fGpu->glslGeneration() >= k150_GrGLSLGeneration);
|
| - vertexBuilder->fGSHeader.append("layout(triangles) in;\n"
|
| - "layout(triangle_strip, max_vertices = 6) out;\n");
|
| - vertexBuilder->gsCodeAppend("\tfor (int i = 0; i < 3; ++i) {\n"
|
| - "\t\tgl_Position = gl_in[i].gl_Position;\n");
|
| - if (fDesc.getHeader().fEmitsPointSize) {
|
| - vertexBuilder->gsCodeAppend("\t\tgl_PointSize = 1.0;\n");
|
| - }
|
| - SkASSERT(vertexBuilder->fGSInputs.count() == vertexBuilder->fGSOutputs.count());
|
| - int count = vertexBuilder->fGSInputs.count();
|
| - for (int i = 0; i < count; ++i) {
|
| - vertexBuilder->gsCodeAppendf("\t\t%s = %s[i];\n",
|
| - vertexBuilder->fGSOutputs[i].getName().c_str(),
|
| - vertexBuilder->fGSInputs[i].getName().c_str());
|
| - }
|
| - vertexBuilder->gsCodeAppend("\t\tEmitVertex();\n"
|
| - "\t}\n"
|
| - "\tEndPrimitive();\n");
|
| - }
|
| -#endif
|
| -}
|
| -
|
| -const char* GrGLProgram::adjustInColor(const SkString& inColor) const {
|
| - if (inColor.size()) {
|
| - return inColor.c_str();
|
| - } else {
|
| - if (GrGLProgramDesc::kSolidWhite_ColorInput == fDesc.getHeader().fColorInput) {
|
| - return GrGLSLOnesVecf(4);
|
| - } else {
|
| - return GrGLSLZerosVecf(4);
|
| - }
|
| - }
|
| -}
|
| -
|
| namespace {
|
| -// prints a shader using params similar to glShaderSource
|
| -void print_shader(GrGLint stringCnt,
|
| - const GrGLchar** strings,
|
| - GrGLint* stringLengths) {
|
| - for (int i = 0; i < stringCnt; ++i) {
|
| - if (NULL == stringLengths || stringLengths[i] < 0) {
|
| - GrPrintf(strings[i]);
|
| - } else {
|
| - GrPrintf("%.*s", stringLengths[i], strings[i]);
|
| - }
|
| - }
|
| -}
|
| -
|
| -// Compiles a GL shader, returns shader ID or 0 if failed params have same meaning as glShaderSource
|
| -GrGLuint compile_shader(const GrGLInterface* gli,
|
| - GrGLenum type,
|
| - int stringCnt,
|
| - const char** strings,
|
| - int* stringLengths) {
|
| - SK_TRACE_EVENT1("GrGLProgram::CompileShader",
|
| - "stringCount", SkStringPrintf("%i", stringCnt).c_str());
|
| -
|
| - GrGLuint shader;
|
| - GR_GL_CALL_RET(gli, shader, CreateShader(type));
|
| - if (0 == shader) {
|
| - return 0;
|
| - }
|
| -
|
| - GrGLint compiled = GR_GL_INIT_ZERO;
|
| - GR_GL_CALL(gli, ShaderSource(shader, stringCnt, strings, stringLengths));
|
| - GR_GL_CALL(gli, CompileShader(shader));
|
| - GR_GL_CALL(gli, GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
|
| -
|
| - if (!compiled) {
|
| - GrGLint infoLen = GR_GL_INIT_ZERO;
|
| - GR_GL_CALL(gli, GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
|
| - SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
|
| - if (infoLen > 0) {
|
| - // retrieve length even though we don't need it to workaround bug in chrome cmd buffer
|
| - // param validation.
|
| - GrGLsizei length = GR_GL_INIT_ZERO;
|
| - GR_GL_CALL(gli, GetShaderInfoLog(shader, infoLen+1,
|
| - &length, (char*)log.get()));
|
| - print_shader(stringCnt, strings, stringLengths);
|
| - GrPrintf("\n%s", log.get());
|
| - }
|
| - SkDEBUGFAIL("Shader compilation failed!");
|
| - GR_GL_CALL(gli, DeleteShader(shader));
|
| - return 0;
|
| - }
|
| - return shader;
|
| -}
|
| -
|
| -// helper version of above for when shader is already flattened into a single SkString
|
| -GrGLuint compile_shader(const GrGLInterface* gli, GrGLenum type, const SkString& shader) {
|
| - const GrGLchar* str = shader.c_str();
|
| - int length = shader.size();
|
| - return compile_shader(gli, type, 1, &str, &length);
|
| -}
|
|
|
| void expand_known_value4f(SkString* string, GrSLConstantVec vec) {
|
| SkASSERT(string->isEmpty() == (vec != kNone_GrSLConstantVec));
|
| @@ -397,50 +207,6 @@ void expand_known_value4f(SkString* string, GrSLConstantVec vec) {
|
|
|
| }
|
|
|
| -// compiles all the shaders from builder and stores the shader IDs
|
| -bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) {
|
| -
|
| - SkASSERT(!fVShaderID);
|
| - SkASSERT(!fGShaderID);
|
| - SkASSERT(!fFShaderID);
|
| -
|
| - SkString shader;
|
| - if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
|
| - vertexBuilder->vsGetShader(&shader);
|
| - if (c_PrintShaders) {
|
| - GrPrintf(shader.c_str());
|
| - GrPrintf("\n");
|
| - }
|
| - if (!(fVShaderID = compile_shader(fGpu->glInterface(), GR_GL_VERTEX_SHADER, shader))) {
|
| - return false;
|
| - }
|
| -
|
| -#if GR_GL_EXPERIMENTAL_GS
|
| - if (fDesc.getHeader().fExperimentalGS) {
|
| - vertexBuilder->gsGetShader(&shader);
|
| - if (c_PrintShaders) {
|
| - GrPrintf(shader.c_str());
|
| - GrPrintf("\n");
|
| - }
|
| - if (!(fGShaderID = compile_shader(fGpu->glInterface(), GR_GL_GEOMETRY_SHADER, shader))) {
|
| - return false;
|
| - }
|
| - }
|
| -#endif
|
| - }
|
| -
|
| - builder.fsGetShader(&shader);
|
| - if (c_PrintShaders) {
|
| - GrPrintf(shader.c_str());
|
| - GrPrintf("\n");
|
| - }
|
| - if (!(fFShaderID = compile_shader(fGpu->glInterface(), GR_GL_FRAGMENT_SHADER, shader))) {
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
| const GrEffectStage* coverageStages[]) {
|
| SkASSERT(0 == fProgramID);
|
| @@ -449,42 +215,14 @@ bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
|
|
| bool needsVertexShader = true;
|
|
|
| - GrGLShaderBuilder builder(fGpu->ctxInfo(), fUniformManager, fDesc, needsVertexShader);
|
| -
|
| + GrGLShaderBuilder builder(fGpu, fUniformManager, fDesc, needsVertexShader);
|
| if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
|
| - const char* viewMName;
|
| - fUniformHandles.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_Visibility,
|
| - kMat33f_GrSLType, "ViewM", &viewMName);
|
| -
|
| - vertexBuilder->vsCodeAppendf("\tvec3 pos3 = %s * vec3(%s, 1);\n"
|
| - "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n",
|
| - viewMName, vertexBuilder->positionAttribute().c_str());
|
| -
|
| - // we output point size in the GS if present
|
| - if (header.fEmitsPointSize
|
| -#if GR_GL_EXPERIMENTAL_GS
|
| - && !header.fExperimentalGS
|
| -#endif
|
| - ) {
|
| - vertexBuilder->vsCodeAppend("\tgl_PointSize = 1.0;\n");
|
| - }
|
| - }
|
| -
|
| - // the dual source output has no canonical var name, have to
|
| - // declare an output, which is incompatible with gl_FragColor/gl_FragData.
|
| - bool dualSourceOutputWritten = false;
|
| -
|
| - GrGLShaderVar colorOutput;
|
| - bool isColorDeclared = GrGLSLSetupFSColorOuput(fGpu->glslGeneration(),
|
| - declared_color_output_name(),
|
| - &colorOutput);
|
| - if (isColorDeclared) {
|
| - builder.fsOutputAppend(colorOutput);
|
| + fUniformHandles.fViewMatrixUni = vertexBuilder->getViewMatrixUniform();
|
| }
|
|
|
| // incoming color to current stage being processed.
|
| - SkString inColor;
|
| - GrSLConstantVec knownColorValue = this->genInputColor(&builder, &inColor);
|
| + SkString inColor = builder.getInputColor();
|
| + GrSLConstantVec knownColorValue = builder.getKnownColorValue();
|
|
|
| // Get the coeffs for the Mode-based color filter, determine if color is needed.
|
| SkXfermode::Coeff colorCoeff;
|
| @@ -544,8 +282,8 @@ bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
|
|
| ///////////////////////////////////////////////////////////////////////////
|
| // compute the partial coverage
|
| - SkString inCoverage;
|
| - GrSLConstantVec knownCoverageValue = this->genInputCoverage(&builder, &inCoverage);
|
| + SkString inCoverage = builder.getInputCoverage();
|
| + GrSLConstantVec knownCoverageValue = builder.getKnownCoverageValue();
|
|
|
| for (int e = 0; e < fDesc.numCoverageEffects(); ++e) {
|
| effectUniformArrays[e] = &fCoverageEffects[e].fSamplerUnis;
|
| @@ -576,9 +314,8 @@ bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
| GrGLProgramDesc::CoverageOutput coverageOutput =
|
| static_cast<GrGLProgramDesc::CoverageOutput>(header.fCoverageOutput);
|
| if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(coverageOutput)) {
|
| - builder.fsOutputAppend().set(kVec4f_GrSLType,
|
| - GrGLShaderVar::kOut_TypeModifier,
|
| - dual_source_output_name());
|
| + const char* secondaryOutputName = builder.enableSecondaryOutput();
|
| +
|
| // default coeff to ones for kCoverage_DualSrcOutput
|
| SkString coeff;
|
| GrSLConstantVec knownCoeffValue = kOnes_GrSLConstantVec;
|
| @@ -613,8 +350,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
| knownCoeffValue,
|
| knownCoverageValue,
|
| false);
|
| - builder.fsCodeAppendf("\t%s = %s;\n", dual_source_output_name(), modulate.c_str());
|
| - dualSourceOutputWritten = true;
|
| + builder.fsCodeAppendf("\t%s = %s;\n", secondaryOutputName, modulate.c_str());
|
| }
|
|
|
| ///////////////////////////////////////////////////////////////////////////
|
| @@ -655,33 +391,17 @@ bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
| } else {
|
| expand_known_value4f(&fragColor, knownFragColorValue);
|
| }
|
| - builder.fsCodeAppendf("\t%s = %s;\n", colorOutput.getName().c_str(), fragColor.c_str());
|
| -
|
| - ///////////////////////////////////////////////////////////////////////////
|
| - // insert GS
|
| -#ifdef SK_DEBUG
|
| - if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
|
| - this->genGeometryShader(vertexBuilder);
|
| - }
|
| -#endif
|
| + builder.fsCodeAppendf("\t%s = %s;\n", builder.getColorOutputName(), fragColor.c_str());
|
|
|
| - ///////////////////////////////////////////////////////////////////////////
|
| - // compile and setup attribs and unis
|
| -
|
| - if (!this->compileShaders(builder)) {
|
| - return false;
|
| - }
|
| -
|
| - if (!this->bindOutputsAttribsAndLinkProgram(builder,
|
| - isColorDeclared,
|
| - dualSourceOutputWritten)) {
|
| + if (!builder.finish(&fProgramID)) {
|
| return false;
|
| }
|
|
|
| - builder.finished(fProgramID);
|
| fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
|
| fUniformHandles.fDstCopyTopLeftUni = builder.getDstCopyTopLeftUniform();
|
| fUniformHandles.fDstCopyScaleUni = builder.getDstCopyScaleUniform();
|
| + fUniformHandles.fColorUni = builder.getColorUniform();
|
| + fUniformHandles.fCoverageUni = builder.getCoverageUniform();
|
| fUniformHandles.fDstCopySamplerUni = builder.getDstCopySamplerUniform();
|
| // This must be called after we set fDstCopySamplerUni above.
|
| this->initSamplerUniforms();
|
| @@ -689,82 +409,6 @@ bool GrGLProgram::genProgram(const GrEffectStage* colorStages[],
|
| return true;
|
| }
|
|
|
| -bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
|
| - bool bindColorOut,
|
| - bool bindDualSrcOut) {
|
| - GL_CALL_RET(fProgramID, CreateProgram());
|
| - if (!fProgramID) {
|
| - return false;
|
| - }
|
| -
|
| - if (fVShaderID) {
|
| - GL_CALL(AttachShader(fProgramID, fVShaderID));
|
| - }
|
| - if (fGShaderID) {
|
| - GL_CALL(AttachShader(fProgramID, fGShaderID));
|
| - }
|
| - GL_CALL(AttachShader(fProgramID, fFShaderID));
|
| -
|
| - if (bindColorOut) {
|
| - GL_CALL(BindFragDataLocation(fProgramID, 0, declared_color_output_name()));
|
| - }
|
| - if (bindDualSrcOut) {
|
| - GL_CALL(BindFragDataLocationIndexed(fProgramID, 0, 1, dual_source_output_name()));
|
| - }
|
| -
|
| - const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader();
|
| -
|
| - // Bind the attrib locations to same values for all shaders
|
| - if (GrGLShaderBuilder::VertexBuilder* vertexBuilder = builder.getVertexBuilder()) {
|
| - GL_CALL(BindAttribLocation(fProgramID,
|
| - header.fPositionAttributeIndex,
|
| - vertexBuilder->positionAttribute().c_str()));
|
| - if (-1 != header.fLocalCoordAttributeIndex) {
|
| - GL_CALL(BindAttribLocation(fProgramID,
|
| - header.fLocalCoordAttributeIndex,
|
| - vertexBuilder->localCoordsAttribute().c_str()));
|
| - }
|
| - if (-1 != header.fColorAttributeIndex) {
|
| - GL_CALL(BindAttribLocation(fProgramID, header.fColorAttributeIndex, COL_ATTR_NAME));
|
| - }
|
| - if (-1 != header.fCoverageAttributeIndex) {
|
| - GL_CALL(BindAttribLocation(fProgramID, header.fCoverageAttributeIndex, COV_ATTR_NAME));
|
| - }
|
| -
|
| - const GrGLShaderBuilder::VertexBuilder::AttributePair* attribEnd = vertexBuilder->getEffectAttributes().end();
|
| - for (const GrGLShaderBuilder::VertexBuilder::AttributePair* attrib = vertexBuilder->getEffectAttributes().begin();
|
| - attrib != attribEnd;
|
| - ++attrib) {
|
| - GL_CALL(BindAttribLocation(fProgramID, attrib->fIndex, attrib->fName.c_str()));
|
| - }
|
| - }
|
| -
|
| - GL_CALL(LinkProgram(fProgramID));
|
| -
|
| - GrGLint linked = GR_GL_INIT_ZERO;
|
| - GL_CALL(GetProgramiv(fProgramID, GR_GL_LINK_STATUS, &linked));
|
| - if (!linked) {
|
| - GrGLint infoLen = GR_GL_INIT_ZERO;
|
| - GL_CALL(GetProgramiv(fProgramID, GR_GL_INFO_LOG_LENGTH, &infoLen));
|
| - SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
|
| - if (infoLen > 0) {
|
| - // retrieve length even though we don't need it to workaround
|
| - // bug in chrome cmd buffer param validation.
|
| - GrGLsizei length = GR_GL_INIT_ZERO;
|
| - GL_CALL(GetProgramInfoLog(fProgramID,
|
| - infoLen+1,
|
| - &length,
|
| - (char*)log.get()));
|
| - GrPrintf((char*)log.get());
|
| - }
|
| - SkDEBUGFAIL("Error linking program");
|
| - GL_CALL(DeleteProgram(fProgramID));
|
| - fProgramID = 0;
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| void GrGLProgram::initSamplerUniforms() {
|
| GL_CALL(UseProgram(fProgramID));
|
| GrGLint texUnitIdx = 0;
|
|
|