From 2db3ded335fdb6697623bece61cabc307a414770 Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Wed, 22 May 2013 14:34:04 +0000 Subject: Make GrGLProgramDesc's key variable length by compacting the effect key array R=robertphillips@google.com Review URL: https://codereview.chromium.org/15252004 git-svn-id: http://skia.googlecode.com/svn/trunk@9239 2bbb7eff-a529-9590-31e7-b0007b416f81 --- src/gpu/gl/GrGLProgram.cpp | 136 ++++++++++++---------- src/gpu/gl/GrGLProgram.h | 12 +- src/gpu/gl/GrGLProgramDesc.cpp | 245 +++++++++++++++++++++++---------------- src/gpu/gl/GrGLProgramDesc.h | 145 ++++++++++++++++++----- src/gpu/gl/GrGLShaderBuilder.cpp | 20 ++-- src/gpu/gl/GrGpuGL.h | 41 +++---- src/gpu/gl/GrGpuGL_program.cpp | 161 ++++++++++++++++++------- 7 files changed, 484 insertions(+), 276 deletions(-) (limited to 'src/gpu/gl') diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp index 1b9c3a293a..0445073d4c 100644 --- a/src/gpu/gl/GrGLProgram.cpp +++ b/src/gpu/gl/GrGLProgram.cpp @@ -96,7 +96,7 @@ void GrGLProgram::abandon() { void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const { - switch (fDesc.fCoverageOutput) { + switch (fDesc.getHeader().fCoverageOutput) { case GrGLProgramDesc::kModulate_CoverageOutput: break; // The prog will write a coverage value to the secondary @@ -221,7 +221,7 @@ void add_color_filter(GrGLShaderBuilder* builder, } GrSLConstantVec GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) { - switch (fDesc.fColorInput) { + switch (fDesc.getHeader().fColorInput) { case GrGLProgramDesc::kAttribute_ColorInput: { builder->addAttribute(kVec4f_GrSLType, COL_ATTR_NAME); const char *vsName, *fsName; @@ -250,7 +250,7 @@ GrSLConstantVec GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* } GrSLConstantVec GrGLProgram::genInputCoverage(GrGLShaderBuilder* builder, SkString* inCoverage) { - switch (fDesc.fCoverageInput) { + switch (fDesc.getHeader().fCoverageInput) { case GrGLProgramDesc::kAttribute_ColorInput: { builder->addAttribute(kVec4f_GrSLType, COV_ATTR_NAME); const char *vsName, *fsName; @@ -282,13 +282,13 @@ GrSLConstantVec GrGLProgram::genInputCoverage(GrGLShaderBuilder* builder, SkStri void GrGLProgram::genGeometryShader(GrGLShaderBuilder* builder) const { #if GR_GL_EXPERIMENTAL_GS // TODO: The builder should add all this glue code. - if (fDesc.fExperimentalGS) { + if (fDesc.getHeader().fExperimentalGS) { GrAssert(fContext.info().glslGeneration() >= k150_GrGLSLGeneration); builder->fGSHeader.append("layout(triangles) in;\n" "layout(triangle_strip, max_vertices = 6) out;\n"); builder->gsCodeAppend("\tfor (int i = 0; i < 3; ++i) {\n" "\t\tgl_Position = gl_in[i].gl_Position;\n"); - if (fDesc.fEmitsPointSize) { + if (fDesc.getHeader().fEmitsPointSize) { builder->gsCodeAppend("\t\tgl_PointSize = 1.0;\n"); } GrAssert(builder->fGSInputs.count() == builder->fGSOutputs.count()); @@ -309,7 +309,7 @@ const char* GrGLProgram::adjustInColor(const SkString& inColor) const { if (inColor.size()) { return inColor.c_str(); } else { - if (GrGLProgramDesc::kSolidWhite_ColorInput == fDesc.fColorInput) { + if (GrGLProgramDesc::kSolidWhite_ColorInput == fDesc.getHeader().fColorInput) { return GrGLSLOnesVecf(4); } else { return GrGLSLZerosVecf(4); @@ -412,7 +412,7 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) { fGShaderID = 0; #if GR_GL_EXPERIMENTAL_GS - if (fDesc.fExperimentalGS) { + if (fDesc.getHeader().fExperimentalGS) { builder.getShader(GrGLShaderBuilder::kGeometry_ShaderType, &shader); if (c_PrintShaders) { GrPrintf(shader.c_str()); @@ -439,6 +439,8 @@ bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) { bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { GrAssert(0 == fProgramID); + const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); + GrGLShaderBuilder builder(fContext.info(), fUniformManager, fDesc); // the dual source output has no canonical var name, have to @@ -467,9 +469,9 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { GrSLConstantVec knownColorValue = this->genInputColor(&builder, &inColor); // we output point size in the GS if present - if (fDesc.fEmitsPointSize + if (header.fEmitsPointSize #if GR_GL_EXPERIMENTAL_GS - && !fDesc.fExperimentalGS + && !header.fExperimentalGS #endif ) { builder.vsCodeAppend("\tgl_PointSize = 1.0;\n"); @@ -479,7 +481,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { SkXfermode::Coeff colorCoeff; SkXfermode::Coeff filterColorCoeff; SkAssertResult( - SkXfermode::ModeAsCoeff(static_cast(fDesc.fColorFilterXfermode), + SkXfermode::ModeAsCoeff(static_cast(header.fColorFilterXfermode), &filterColorCoeff, &colorCoeff)); bool needColor, needFilterColor; @@ -489,13 +491,13 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { SkTArray* stageUniformArrays[GrDrawState::kNumStages]; if (needColor) { - for (int s = 0; s < fDesc.fFirstCoverageStage; ++s) { + for (int s = 0; s < fDesc.numColorEffects(); ++s) { stageUniformArrays[s] = &fUniformHandles.fEffectSamplerUnis[s]; } builder.emitEffects(stages, - fDesc.fEffectKeys, - fDesc.fFirstCoverageStage, + fDesc.effectKeys(), + fDesc.numColorEffects(), &inColor, &knownColorValue, stageUniformArrays, @@ -503,7 +505,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { } // Insert the color filter. This will soon be replaced by a color effect. - if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) { + if (SkXfermode::kDst_Mode != header.fColorFilterXfermode) { const char* colorFilterColorUniName = NULL; fUniformHandles.fColorFilterUni = builder.addUniform(GrGLShaderBuilder::kFragment_ShaderType, kVec4f_GrSLType, "FilterColor", @@ -529,20 +531,20 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { SkString inCoverage; GrSLConstantVec knownCoverageValue = this->genInputCoverage(&builder, &inCoverage); - for (int s = fDesc.fFirstCoverageStage, i = 0; s < GrDrawState::kNumStages; ++s, ++i) { - stageUniformArrays[i] = &fUniformHandles.fEffectSamplerUnis[s]; + for (int s = 0; s < fDesc.numCoverageEffects(); ++s) { + stageUniformArrays[s] = &fUniformHandles.fEffectSamplerUnis[s + fDesc.numColorEffects()]; } - builder.emitEffects(stages + fDesc.fFirstCoverageStage, - fDesc.fEffectKeys + fDesc.fFirstCoverageStage, - GrDrawState::kNumStages - fDesc.fFirstCoverageStage, + builder.emitEffects(stages + fDesc.numColorEffects(), + fDesc.getEffectKeys() + fDesc.numColorEffects(), + fDesc.numCoverageEffects(), &inCoverage, &knownCoverageValue, stageUniformArrays, - fEffects + fDesc.fFirstCoverageStage); + fEffects + fDesc.numColorEffects()); // discard if coverage is zero - if (fDesc.fDiscardIfZeroCoverage && kOnes_GrSLConstantVec != knownCoverageValue) { + if (header.fDiscardIfZeroCoverage && kOnes_GrSLConstantVec != knownCoverageValue) { if (kZeros_GrSLConstantVec == knownCoverageValue) { // This is unfortunate. builder.fsCodeAppend("\tdiscard;\n"); @@ -553,7 +555,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { } GrGLProgramDesc::CoverageOutput coverageOutput = - static_cast(fDesc.fCoverageOutput); + static_cast(header.fCoverageOutput); if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(coverageOutput)) { builder.fFSOutputs.push_back().set(kVec4f_GrSLType, GrGLShaderVar::kOut_TypeModifier, @@ -561,7 +563,7 @@ bool GrGLProgram::genProgram(const GrEffectStage* stages[]) { // default coeff to ones for kCoverage_DualSrcOutput SkString coeff; GrSLConstantVec knownCoeffValue = kOnes_GrSLConstantVec; - if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == fDesc.fCoverageOutput) { + if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == header.fCoverageOutput) { // Get (1-A) into coeff SkString inColorAlpha; GrGLSLGetComponent4f(&inColorAlpha, @@ -687,20 +689,22 @@ bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& buil 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 GL_CALL(BindAttribLocation(fProgramID, - fDesc.fPositionAttributeIndex, + header.fPositionAttributeIndex, builder.positionAttribute().c_str())); - if (-1 != fDesc.fLocalCoordAttributeIndex) { + if (-1 != header.fLocalCoordAttributeIndex) { GL_CALL(BindAttribLocation(fProgramID, - fDesc.fLocalCoordAttributeIndex, + header.fLocalCoordAttributeIndex, builder.localCoordsAttribute().c_str())); } - if (-1 != fDesc.fColorAttributeIndex) { - GL_CALL(BindAttribLocation(fProgramID, fDesc.fColorAttributeIndex, COL_ATTR_NAME)); + if (-1 != header.fColorAttributeIndex) { + GL_CALL(BindAttribLocation(fProgramID, header.fColorAttributeIndex, COL_ATTR_NAME)); } - if (-1 != fDesc.fCoverageAttributeIndex) { - GL_CALL(BindAttribLocation(fProgramID, fDesc.fCoverageAttributeIndex, COV_ATTR_NAME)); + if (-1 != header.fCoverageAttributeIndex) { + GL_CALL(BindAttribLocation(fProgramID, header.fCoverageAttributeIndex, COV_ATTR_NAME)); } const GrGLShaderBuilder::AttributePair* attribEnd = builder.getEffectAttributes().end(); @@ -761,12 +765,25 @@ void GrGLProgram::initSamplerUniforms() { /////////////////////////////////////////////////////////////////////////////// void GrGLProgram::setData(GrGpuGL* gpu, - GrColor color, - GrColor coverage, + GrDrawState::BlendOptFlags blendOpts, + const GrEffectStage* stages[], const GrDeviceCoordTexture* dstCopy, SharedGLState* sharedState) { const GrDrawState& drawState = gpu->getDrawState(); + GrColor color; + GrColor coverage; + if (blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag) { + color = 0; + coverage = 0; + } else if (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) { + color = 0xffffffff; + coverage = drawState.getCoverage(); + } else { + color = drawState.getColor(); + coverage = drawState.getCoverage(); + } + this->setColor(drawState, color, sharedState); this->setCoverage(drawState, coverage, sharedState); this->setMatrixAndRenderTargetHeight(drawState); @@ -803,26 +820,25 @@ void GrGLProgram::setData(GrGpuGL* gpu, fUniformHandles.fDstCopySamplerUni); } } else { - GrAssert(GrGLUniformManager::kInvalidUniformHandle == - fUniformHandles.fDstCopyTopLeftUni); - GrAssert(GrGLUniformManager::kInvalidUniformHandle == - fUniformHandles.fDstCopyScaleUni); - GrAssert(GrGLUniformManager::kInvalidUniformHandle == - fUniformHandles.fDstCopySamplerUni); - } - for (int s = 0; s < GrDrawState::kNumStages; ++s) { - if (NULL != fEffects[s]) { - const GrEffectStage& stage = drawState.getStage(s); - GrAssert(NULL != stage.getEffect()); - - bool explicitLocalCoords = -1 != fDesc.fLocalCoordAttributeIndex; - GrDrawEffect drawEffect(stage, explicitLocalCoords); - fEffects[s]->setData(fUniformManager, drawEffect); - int numSamplers = fUniformHandles.fEffectSamplerUnis[s].count(); + GrAssert(GrGLUniformManager::kInvalidUniformHandle == fUniformHandles.fDstCopyTopLeftUni); + GrAssert(GrGLUniformManager::kInvalidUniformHandle == fUniformHandles.fDstCopyScaleUni); + GrAssert(GrGLUniformManager::kInvalidUniformHandle == fUniformHandles.fDstCopySamplerUni); + } + + int numEffects = fDesc.numTotalEffects(); + for (int e = 0; e < numEffects; ++e) { + GrAssert(NULL != stages[e]); + // We may have omitted the GrGLEffect because of the color filter logic in genProgram. + // This can be removed when the color filter is an effect. + if (NULL != fEffects[e]) { + bool explicitLocalCoords = -1 != fDesc.getHeader().fLocalCoordAttributeIndex; + GrDrawEffect drawEffect(*stages[e], explicitLocalCoords); + fEffects[e]->setData(fUniformManager, drawEffect); + int numSamplers = fUniformHandles.fEffectSamplerUnis[e].count(); for (int u = 0; u < numSamplers; ++u) { - UniformHandle handle = fUniformHandles.fEffectSamplerUnis[s][u]; + UniformHandle handle = fUniformHandles.fEffectSamplerUnis[e][u]; if (GrGLUniformManager::kInvalidUniformHandle != handle) { - const GrTextureAccess& access = (*stage.getEffect())->textureAccess(u); + const GrTextureAccess& access = (*stages[e]->getEffect())->textureAccess(u); GrGLTexture* texture = static_cast(access.getTexture()); gpu->bindTexture(texUnitIdx, access.getParams(), texture); ++texUnitIdx; @@ -835,18 +851,19 @@ void GrGLProgram::setData(GrGpuGL* gpu, void GrGLProgram::setColor(const GrDrawState& drawState, GrColor color, SharedGLState* sharedState) { + const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); if (!drawState.hasColorVertexAttribute()) { - switch (fDesc.fColorInput) { + switch (header.fColorInput) { case GrGLProgramDesc::kAttribute_ColorInput: - GrAssert(-1 != fDesc.fColorAttributeIndex); + GrAssert(-1 != header.fColorAttributeIndex); if (sharedState->fConstAttribColor != color || - sharedState->fConstAttribColorIndex != fDesc.fColorAttributeIndex) { + sharedState->fConstAttribColorIndex != header.fColorAttributeIndex) { // OpenGL ES only supports the float varieties of glVertexAttrib GrGLfloat c[4]; GrColorToRGBAFloat(color, c); - GL_CALL(VertexAttrib4fv(fDesc.fColorAttributeIndex, c)); + GL_CALL(VertexAttrib4fv(header.fColorAttributeIndex, c)); sharedState->fConstAttribColor = color; - sharedState->fConstAttribColorIndex = fDesc.fColorAttributeIndex; + sharedState->fConstAttribColorIndex = header.fColorAttributeIndex; } break; case GrGLProgramDesc::kUniform_ColorInput: @@ -876,17 +893,18 @@ void GrGLProgram::setColor(const GrDrawState& drawState, void GrGLProgram::setCoverage(const GrDrawState& drawState, GrColor coverage, SharedGLState* sharedState) { + const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); if (!drawState.hasCoverageVertexAttribute()) { - switch (fDesc.fCoverageInput) { + switch (header.fCoverageInput) { case GrGLProgramDesc::kAttribute_ColorInput: if (sharedState->fConstAttribCoverage != coverage || - sharedState->fConstAttribCoverageIndex != fDesc.fCoverageAttributeIndex) { + sharedState->fConstAttribCoverageIndex != header.fCoverageAttributeIndex) { // OpenGL ES only supports the float varieties of glVertexAttrib GrGLfloat c[4]; GrColorToRGBAFloat(coverage, c); - GL_CALL(VertexAttrib4fv(fDesc.fCoverageAttributeIndex, c)); + GL_CALL(VertexAttrib4fv(header.fCoverageAttributeIndex, c)); sharedState->fConstAttribCoverage = coverage; - sharedState->fConstAttribCoverageIndex = fDesc.fCoverageAttributeIndex; + sharedState->fConstAttribCoverageIndex = header.fCoverageAttributeIndex; } break; case GrGLProgramDesc::kUniform_ColorInput: diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h index 578b4a99cf..be6e6878df 100644 --- a/src/gpu/gl/GrGLProgram.h +++ b/src/gpu/gl/GrGLProgram.h @@ -48,7 +48,7 @@ public: void abandon(); /** - * The shader may modify the blend coefficients. Params are in/out + * The shader may modify the blend coefficients. Params are in/out. */ void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const; @@ -104,12 +104,10 @@ public: * This function uploads uniforms and calls each GrGLEffect's setData. It is called before a * draw occurs using the program after the program has already been bound. It also uses the * GrGpuGL object to bind the textures required by the GrGLEffects. - * - * The color and coverage params override the GrDrawState's getColor() and getCoverage() values. */ void setData(GrGpuGL*, - GrColor color, - GrColor coverage, + GrDrawState::BlendOptFlags, + const GrEffectStage* stages[], // output of GrGLProgramDesc:Build() const GrDeviceCoordTexture* dstCopy, // can be NULL SharedGLState*); @@ -121,7 +119,9 @@ private: bool succeeded() const { return 0 != fProgramID; } /** - * This is the heavy initialization routine for building a GLProgram. + * This is the heavy initialization routine for building a GLProgram. stages is all the enabled + * color stages followed by all the enabled coverage stages as output by + * GrGLProgramDesc::Build() */ bool genProgram(const GrEffectStage* stages[]); diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp index e8812fade7..276f9b5a09 100644 --- a/src/gpu/gl/GrGLProgramDesc.cpp +++ b/src/gpu/gl/GrGLProgramDesc.cpp @@ -12,6 +12,8 @@ #include "GrGLShaderBuilder.h" #include "GrGpuGL.h" +#include "SkChecksum.h" + void GrGLProgramDesc::Build(const GrDrawState& drawState, bool isPoints, GrDrawState::BlendOptFlags blendOpts, @@ -19,8 +21,8 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, GrBlendCoeff dstCoeff, const GrGpuGL* gpu, const GrDeviceCoordTexture* dstCopy, + const GrEffectStage* stages[], GrGLProgramDesc* desc) { - // This should already have been caught GrAssert(!(GrDrawState::kSkipDraw_BlendOptFlag & blendOpts)); @@ -34,131 +36,169 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, // bindings in use or other descriptor field settings) it should be set // to a canonical value to avoid duplicate programs with different keys. - - desc->fEmitsPointSize = isPoints; - bool requiresColorAttrib = !skipColor && drawState.hasColorVertexAttribute(); bool requiresCoverageAttrib = !skipCoverage && drawState.hasCoverageVertexAttribute(); // we only need the local coords if we're actually going to generate effect code bool requiresLocalCoordAttrib = !(skipCoverage && skipColor) && drawState.hasLocalCoordAttribute(); - // fColorInput/fCoverageInput records how colors are specified for the program so we strip the - // bits from the bindings to avoid false negatives when searching for an existing program in the - // cache. - - desc->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode(); - - bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) || (!requiresColorAttrib && 0xffffffff == drawState.getColor()); - if (colorIsTransBlack) { - desc->fColorInput = kTransBlack_ColorInput; - } else if (colorIsSolidWhite) { - desc->fColorInput = kSolidWhite_ColorInput; - } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) { - desc->fColorInput = kUniform_ColorInput; - } else { - desc->fColorInput = kAttribute_ColorInput; - } - bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage(); + // Do an initial loop over the stages to count them. We count the color and coverage effects + // separately here. Later we may decide the distinction doesn't matter and will count all + // effects as color in desc. Two things will allow simplication of this mess: GrDrawState will + // have tight lists of color and coverage stages rather than a fixed size array with NULLS and + // the xfermode-color filter will be removed. + int colorEffectCnt = 0; + int coverageEffectCnt = 0; + if (!skipColor) { + for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) { + if (drawState.isStageEnabled(s)) { + stages[colorEffectCnt] = &drawState.getStage(s); + ++colorEffectCnt; + } + } + } + if (!skipCoverage) { + for (int s = drawState.getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) { + if (drawState.isStageEnabled(s)) { + stages[colorEffectCnt + coverageEffectCnt] = &drawState.getStage(s); + ++coverageEffectCnt; + } + } + } - if (skipCoverage) { - desc->fCoverageInput = kTransBlack_ColorInput; - } else if (covIsSolidWhite) { - desc->fCoverageInput = kSolidWhite_ColorInput; - } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) { - desc->fCoverageInput = kUniform_ColorInput; - } else { - desc->fCoverageInput = kAttribute_ColorInput; + size_t newKeyLength = KeyLength(colorEffectCnt + coverageEffectCnt); + bool allocChanged; + desc->fKey.reset(newKeyLength, SkAutoMalloc::kAlloc_OnShrink, &allocChanged); + if (allocChanged || !desc->fInitialized) { + // make sure any padding in the header is zero if we we haven't used this allocation before. + memset(desc->header(), 0, kHeaderSize); } + // write the key length + *desc->atOffset() = newKeyLength; + + KeyHeader* header = desc->header(); + EffectKey* effectKeys = desc->effectKeys(); + int currEffectKey = 0; bool readsDst = false; bool readFragPosition = false; - int lastEnabledStage = -1; - for (int s = 0; s < GrDrawState::kNumStages; ++s) { - bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage; if (!skip && drawState.isStageEnabled(s)) { - lastEnabledStage = s; const GrEffectRef& effect = *drawState.getStage(s).getEffect(); const GrBackendEffectFactory& factory = effect->getFactory(); GrDrawEffect drawEffect(drawState.getStage(s), requiresLocalCoordAttrib); - desc->fEffectKeys[s] = factory.glEffectKey(drawEffect, gpu->glCaps()); + effectKeys[currEffectKey] = factory.glEffectKey(drawEffect, gpu->glCaps()); + ++currEffectKey; if (effect->willReadDstColor()) { readsDst = true; } if (effect->willReadFragmentPosition()) { readFragPosition = true; } - } else { - desc->fEffectKeys[s] = 0; } } + header->fEmitsPointSize = isPoints; + header->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode(); + + // Currently the experimental GS will only work with triangle prims (and it doesn't do anything + // other than pass through values from the VS to the FS anyway). +#if GR_GL_EXPERIMENTAL_GS +#if 0 + header->fExperimentalGS = gpu->caps().geometryShaderSupport(); +#else + header->fExperimentalGS = false; +#endif +#endif + if (colorIsTransBlack) { + header->fColorInput = kTransBlack_ColorInput; + } else if (colorIsSolidWhite) { + header->fColorInput = kSolidWhite_ColorInput; + } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) { + header->fColorInput = kUniform_ColorInput; + } else { + header->fColorInput = kAttribute_ColorInput; + } + + bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage(); + + if (skipCoverage) { + header->fCoverageInput = kTransBlack_ColorInput; + } else if (covIsSolidWhite) { + header->fCoverageInput = kSolidWhite_ColorInput; + } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) { + header->fCoverageInput = kUniform_ColorInput; + } else { + header->fCoverageInput = kAttribute_ColorInput; + } + if (readsDst) { GrAssert(NULL != dstCopy || gpu->caps()->dstReadInShaderSupport()); const GrTexture* dstCopyTexture = NULL; if (NULL != dstCopy) { dstCopyTexture = dstCopy->texture(); } - desc->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps()); - GrAssert(0 != desc->fDstReadKey); + header->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps()); + GrAssert(0 != header->fDstReadKey); } else { - desc->fDstReadKey = 0; + header->fDstReadKey = 0; } if (readFragPosition) { - desc->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(), + header->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(), gpu->glCaps()); } else { - desc->fFragPosKey = 0; + header->fFragPosKey = 0; } - desc->fCoverageOutput = kModulate_CoverageOutput; - - // Currently the experimental GS will only work with triangle prims (and it doesn't do anything - // other than pass through values from the VS to the FS anyway). -#if GR_GL_EXPERIMENTAL_GS -#if 0 - desc->fExperimentalGS = gpu->caps().geometryShaderSupport(); -#else - desc->fExperimentalGS = false; -#endif -#endif - - // We leave this set to kNumStages until we discover that the coverage/color distinction is - // material to the generated program. We do this to avoid distinct keys that generate equivalent - // programs. - desc->fFirstCoverageStage = GrDrawState::kNumStages; - // This tracks the actual first coverage stage. - int firstCoverageStage = GrDrawState::kNumStages; - desc->fDiscardIfZeroCoverage = false; // Enabled below if stenciling and there is coverage. - bool hasCoverage = false; - // If we're rendering coverage-as-color then it's as though there are no coverage stages. - if (!drawState.isCoverageDrawing()) { - // We can have coverage either through a stage or coverage vertex attributes. - if (drawState.getFirstCoverageStage() <= lastEnabledStage) { - firstCoverageStage = drawState.getFirstCoverageStage(); - hasCoverage = true; - } else { - hasCoverage = requiresCoverageAttrib; - } + // Record attribute indices + header->fPositionAttributeIndex = drawState.positionAttributeIndex(); + header->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex(); + + // For constant color and coverage we need an attribute with an index beyond those already set + int availableAttributeIndex = drawState.getVertexAttribCount(); + if (requiresColorAttrib) { + header->fColorAttributeIndex = drawState.colorVertexAttributeIndex(); + } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) { + GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); + header->fColorAttributeIndex = availableAttributeIndex; + availableAttributeIndex++; + } else { + header->fColorAttributeIndex = -1; + } + + if (requiresCoverageAttrib) { + header->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex(); + } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) { + GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); + header->fCoverageAttributeIndex = availableAttributeIndex; + } else { + header->fCoverageAttributeIndex = -1; } - if (hasCoverage) { + // Here we deal with whether/how we handle color and coverage separately. + + // Set these defaults and then possibly change our mind if there is coverage. + header->fDiscardIfZeroCoverage = false; + header->fCoverageOutput = kModulate_CoverageOutput; + + // If we do have coverage determine whether it matters. + bool separateCoverageFromColor = false; + if (!drawState.isCoverageDrawing() && (coverageEffectCnt > 0 || requiresCoverageAttrib)) { // color filter is applied between color/coverage computation - if (SkXfermode::kDst_Mode != desc->fColorFilterXfermode) { - desc->fFirstCoverageStage = firstCoverageStage; + if (SkXfermode::kDst_Mode != header->fColorFilterXfermode) { + separateCoverageFromColor = true; } // If we're stenciling then we want to discard samples that have zero coverage if (drawState.getStencil().doesWrite()) { - desc->fDiscardIfZeroCoverage = true; - desc->fFirstCoverageStage = firstCoverageStage; + header->fDiscardIfZeroCoverage = true; + separateCoverageFromColor = true; } if (gpu->caps()->dualSourceBlendingSupport() && @@ -166,46 +206,45 @@ void GrGLProgramDesc::Build(const GrDrawState& drawState, GrDrawState::kCoverageAsAlpha_BlendOptFlag))) { if (kZero_GrBlendCoeff == dstCoeff) { // write the coverage value to second color - desc->fCoverageOutput = kSecondaryCoverage_CoverageOutput; - desc->fFirstCoverageStage = firstCoverageStage; + header->fCoverageOutput = kSecondaryCoverage_CoverageOutput; + separateCoverageFromColor = true; } else if (kSA_GrBlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. - desc->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput; - desc->fFirstCoverageStage = firstCoverageStage; + header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput; + separateCoverageFromColor = true; } else if (kSC_GrBlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. - desc->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput; - desc->fFirstCoverageStage = firstCoverageStage; + header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput; + separateCoverageFromColor = true; } } else if (readsDst && kOne_GrBlendCoeff == srcCoeff && kZero_GrBlendCoeff == dstCoeff) { - desc->fCoverageOutput = kCombineWithDst_CoverageOutput; - desc->fFirstCoverageStage = firstCoverageStage; + header->fCoverageOutput = kCombineWithDst_CoverageOutput; + separateCoverageFromColor = true; } } - - desc->fPositionAttributeIndex = drawState.positionAttributeIndex(); - desc->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex(); - - // For constant color and coverage we need an attribute with an index beyond those already set - int availableAttributeIndex = drawState.getVertexAttribCount(); - if (requiresColorAttrib) { - desc->fColorAttributeIndex = drawState.colorVertexAttributeIndex(); - } else if (GrGLProgramDesc::kAttribute_ColorInput == desc->fColorInput) { - GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); - desc->fColorAttributeIndex = availableAttributeIndex; - availableAttributeIndex++; + if (separateCoverageFromColor) { + header->fColorEffectCnt = colorEffectCnt; + header->fCoverageEffectCnt = coverageEffectCnt; } else { - desc->fColorAttributeIndex = -1; + header->fColorEffectCnt = colorEffectCnt + coverageEffectCnt; + header->fCoverageEffectCnt = 0; } - if (requiresCoverageAttrib) { - desc->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex(); - } else if (GrGLProgramDesc::kAttribute_ColorInput == desc->fCoverageInput) { - GrAssert(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); - desc->fCoverageAttributeIndex = availableAttributeIndex; - } else { - desc->fCoverageAttributeIndex = -1; + *desc->checksum() = 0; + *desc->checksum() = SkChecksum::Compute(reinterpret_cast(desc->fKey.get()), + newKeyLength); + desc->fInitialized = true; +} + +GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) { + fInitialized = other.fInitialized; + if (fInitialized) { + size_t keyLength = other.keyLength(); + fKey.reset(keyLength); + memcpy(fKey.get(), other.fKey.get(), keyLength); } + return *this; } + diff --git a/src/gpu/gl/GrGLProgramDesc.h b/src/gpu/gl/GrGLProgramDesc.h index b49cb722c0..f83275dd46 100644 --- a/src/gpu/gl/GrGLProgramDesc.h +++ b/src/gpu/gl/GrGLProgramDesc.h @@ -25,26 +25,37 @@ class GrGpuGL; to be API-neutral then so could this class. */ class GrGLProgramDesc { public: - GrGLProgramDesc() { - // since we use this as part of a key we can't have any uninitialized padding - memset(this, 0, sizeof(GrGLProgramDesc)); - } + GrGLProgramDesc() : fInitialized(false) {} + GrGLProgramDesc(const GrGLProgramDesc& desc) { *this = desc; } - // Returns this as a uint32_t array to be used as a key in the program cache + // Returns this as a uint32_t array to be used as a key in the program cache. const uint32_t* asKey() const { - return reinterpret_cast(this); + GrAssert(fInitialized); + return reinterpret_cast(fKey.get()); } + // Gets the number of bytes in asKey(). It will be a 4-byte aligned value. When comparing two + // keys the size of either key can be used with memcmp() since the lengths themselves begin the + // keys and thus the memcmp will exit early if the keys are of different lengths. + uint32_t keyLength() const { return *this->atOffset(); } + + // Gets the a checksum of the key. Can be used as a hash value for a fast lookup in a cache. + uint32_t getChecksum() const { return *this->atOffset(); } + // For unit testing. void setRandom(SkMWCRandom*, const GrGpuGL* gpu, - const GrTexture* dummyDstTexture, - const GrEffectStage* stages[GrDrawState::kNumStages], + const GrRenderTarget* dummyDstRenderTarget, + const GrTexture* dummyDstCopyTexture, + const GrEffectStage* stages[], + int numColorStages, + int numCoverageStages, int currAttribIndex); /** * Builds a program descriptor from a GrDrawState. Whether the primitive type is points, the - * output of GrDrawState::getBlendOpts, and the caps of the GrGpuGL are also inputs. + * output of GrDrawState::getBlendOpts, and the caps of the GrGpuGL are also inputs. It also + * writes a tightly packed array of GrEffectStage* from the drawState. */ static void Build(const GrDrawState&, bool isPoints, @@ -53,8 +64,37 @@ public: GrBlendCoeff dstCoeff, const GrGpuGL* gpu, const GrDeviceCoordTexture* dstCopy, + const GrEffectStage* outStages[GrDrawState::kNumStages], GrGLProgramDesc* outDesc); + int numColorEffects() const { + GrAssert(fInitialized); + return this->getHeader().fColorEffectCnt; + } + + int numCoverageEffects() const { + GrAssert(fInitialized); + return this->getHeader().fCoverageEffectCnt; + } + + int numTotalEffects() const { return this->numColorEffects() + this->numCoverageEffects(); } + + GrGLProgramDesc& operator= (const GrGLProgramDesc& other); + + bool operator== (const GrGLProgramDesc& other) const { + GrAssert(fInitialized && other.fInitialized); + // The length is masked as a hint to the compiler that the address will be 4 byte aligned. + return 0 == memcmp(this->asKey(), other.asKey(), this->keyLength() & ~0x3); + } + + bool operator!= (const GrGLProgramDesc& other) const { + return !(*this == other); + } + + static bool Less(const GrGLProgramDesc& a, const GrGLProgramDesc& b) { + return memcmp(a.asKey(), b.asKey(), a.keyLength() & ~0x3) < 0; + } + private: // Specifies where the initial color comes from before the stages are applied. enum ColorInput { @@ -96,37 +136,78 @@ private: } } - /** Non-zero if this stage has an effect */ - GrGLEffect::EffectKey fEffectKeys[GrDrawState::kNumStages]; - - // To enable experimental geometry shader code (not for use in - // production) -#if GR_GL_EXPERIMENTAL_GS - bool fExperimentalGS; -#endif - - GrGLShaderBuilder::DstReadKey fDstReadKey; // set by GrGLShaderBuilder if there + struct KeyHeader { + GrGLShaderBuilder::DstReadKey fDstReadKey; // set by GrGLShaderBuilder if there // are effects that must read the dst. // Otherwise, 0. - GrGLShaderBuilder::FragPosKey fFragPosKey; // set by GrGLShaderBuilder if there are + GrGLShaderBuilder::FragPosKey fFragPosKey; // set by GrGLShaderBuilder if there are // effects that read the fragment position. // Otherwise, 0. - // should the FS discard if the coverage is zero (to avoid stencil manipulation) - SkBool8 fDiscardIfZeroCoverage; + // should the FS discard if the coverage is zero (to avoid stencil manipulation) + SkBool8 fDiscardIfZeroCoverage; - uint8_t fColorInput; // casts to enum ColorInput - uint8_t fCoverageInput; // casts to enum ColorInput - uint8_t fCoverageOutput; // casts to enum CoverageOutput + uint8_t fColorInput; // casts to enum ColorInput + uint8_t fCoverageInput; // casts to enum ColorInput + uint8_t fCoverageOutput; // casts to enum CoverageOutput - int8_t fFirstCoverageStage; - SkBool8 fEmitsPointSize; - uint8_t fColorFilterXfermode; // casts to enum SkXfermode::Mode + SkBool8 fEmitsPointSize; + uint8_t fColorFilterXfermode; // casts to enum SkXfermode::Mode + + // To enable experimental geometry shader code (not for use in + // production) +#if GR_GL_EXPERIMENTAL_GS + SkBool8 fExperimentalGS; +#endif + + int8_t fPositionAttributeIndex; + int8_t fLocalCoordAttributeIndex; + int8_t fColorAttributeIndex; + int8_t fCoverageAttributeIndex; + + int8_t fColorEffectCnt; + int8_t fCoverageEffectCnt; + }; + + // The key is 1 uint32_t for the length, followed another for the checksum, the header, and then + // the effect keys. Everything is fixed length except the effect key array. + enum { + kLengthOffset = 0, + kChecksumOffset = kLengthOffset + sizeof(uint32_t), + kHeaderOffset = kChecksumOffset + sizeof(uint32_t), + kHeaderSize = SkAlign4(sizeof(KeyHeader)), + kEffectKeyOffset = kHeaderOffset + kHeaderSize, + }; + + template T* atOffset() { + return reinterpret_cast(reinterpret_cast(fKey.get()) + OFFSET); + } + + template const T* atOffset() const { + return reinterpret_cast(reinterpret_cast(fKey.get()) + OFFSET); + } + + typedef GrGLEffect::EffectKey EffectKey; + + uint32_t* checksum() { return this->atOffset(); } + KeyHeader* header() { return this->atOffset(); } + EffectKey* effectKeys() { return this->atOffset(); } + + const KeyHeader& getHeader() const { return *this->atOffset(); } + const EffectKey* getEffectKeys() const { return this->atOffset(); } + + static size_t KeyLength(int effectCnt) { + GR_STATIC_ASSERT(!(sizeof(EffectKey) & 0x3)); + return kEffectKeyOffset + effectCnt * sizeof(EffectKey); + } + + enum { + kMaxPreallocEffects = 16, + kPreAllocSize = kEffectKeyOffset + kMaxPreallocEffects * sizeof(EffectKey), + }; - int8_t fPositionAttributeIndex; - int8_t fLocalCoordAttributeIndex; - int8_t fColorAttributeIndex; - int8_t fCoverageAttributeIndex; + SkAutoSMalloc fKey; + bool fInitialized; // GrGLProgram and GrGLShaderBuilder read the private fields to generate code. TODO: Move all // code generation to GrGLShaderBuilder (and maybe add getters rather than friending). diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp index ddcc61574c..36eb6d5169 100644 --- a/src/gpu/gl/GrGLShaderBuilder.cpp +++ b/src/gpu/gl/GrGLShaderBuilder.cpp @@ -105,7 +105,7 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo, , fUniformManager(uniformManager) , fFSFeaturesAddedMask(0) #if GR_GL_EXPERIMENTAL_GS - , fUsesGS(desc.fExperimentalGS) + , fUsesGS(SkToBool(desc.getHeader().fExperimentalGS)) #else , fUsesGS(false) #endif @@ -113,11 +113,13 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo, , fRTHeightUniform(GrGLUniformManager::kInvalidUniformHandle) , fDstCopyTopLeftUniform (GrGLUniformManager::kInvalidUniformHandle) , fDstCopyScaleUniform (GrGLUniformManager::kInvalidUniformHandle) - , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == desc.fFragPosKey) { + , fTopLeftFragPosRead(kTopLeftFragPosRead_FragPosKey == desc.getHeader().fFragPosKey) { + + const GrGLProgramDesc::KeyHeader& header = desc.getHeader(); fPositionVar = &fVSAttrs.push_back(); fPositionVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, "aPosition"); - if (-1 != desc.fLocalCoordAttributeIndex) { + if (-1 != header.fLocalCoordAttributeIndex) { fLocalCoordsVar = &fVSAttrs.push_back(); fLocalCoordsVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, @@ -126,13 +128,13 @@ GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctxInfo, fLocalCoordsVar = fPositionVar; } // Emit code to read the dst copy textue if necessary. - if (kNoDstRead_DstReadKey != desc.fDstReadKey && + if (kNoDstRead_DstReadKey != header.fDstReadKey && GrGLCaps::kNone_FBFetchType == ctxInfo.caps()->fbFetchType()) { - bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & desc.fDstReadKey); + bool topDown = SkToBool(kTopLeftOrigin_DstReadKeyBit & header.fDstReadKey); const char* dstCopyTopLeftName; const char* dstCopyCoordScaleName; uint32_t configMask; - if (SkToBool(kUseAlphaConfig_DstReadKeyBit & desc.fDstReadKey)) { + if (SkToBool(kUseAlphaConfig_DstReadKeyBit & header.fDstReadKey)) { configMask = kA_GrColorComponentFlag; } else { configMask = kRGBA_GrColorComponentFlags; @@ -656,11 +658,7 @@ void GrGLShaderBuilder::emitEffects( SkString outColor; for (int e = 0; e < effectCnt; ++e) { - if (NULL == effectStages[e] || GrGLEffect::kNoEffectKey == effectKeys[e]) { - continue; - } - - GrAssert(NULL != effectStages[e]->getEffect()); + GrAssert(NULL != effectStages[e] && NULL != effectStages[e]->getEffect()); const GrEffectStage& stage = *effectStages[e]; const GrEffectRef& effect = *stage.getEffect(); diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h index 0f75280178..8f80a72cbc 100644 --- a/src/gpu/gl/GrGpuGL.h +++ b/src/gpu/gl/GrGpuGL.h @@ -177,47 +177,36 @@ private: void abandon(); GrGLProgram* getProgram(const GrGLProgramDesc& desc, const GrEffectStage* stages[]); + private: enum { - kKeySize = sizeof(GrGLProgramDesc), // We may actually have kMaxEntries+1 shaders in the GL context because we create a new // shader before evicting from the cache. - kMaxEntries = 32 + kMaxEntries = 32, + kHashBits = 6, }; - class Entry; - // The value of the hash key is based on the ProgramDesc. - typedef GrTBinHashKey ProgramHashKey; - - class Entry : public ::GrNoncopyable { - public: - Entry() : fProgram(NULL), fLRUStamp(0) {} - Entry& operator = (const Entry& entry) { - GrSafeRef(entry.fProgram.get()); - fProgram.reset(entry.fProgram.get()); - fKey = entry.fKey; - fLRUStamp = entry.fLRUStamp; - return *this; - } - int compare(const ProgramHashKey& key) const { - return fKey.compare(key); - } + struct Entry; - public: - SkAutoTUnref fProgram; - ProgramHashKey fKey; - unsigned int fLRUStamp; // Move outside entry? - }; + struct ProgDescLess; + + // binary search for entry matching desc. returns index into fEntries that matches desc or ~ + // of the index of where it should be inserted. + int search(const GrGLProgramDesc& desc) const; - GrTHashTable fHashCache; + // sorted array of all the entries + Entry* fEntries[kMaxEntries]; + // hash table based on lowest kHashBits bits of the program key. Used to avoid binary + // searching fEntries. + Entry* fHashTable[1 << kHashBits]; - Entry fEntries[kMaxEntries]; int fCount; unsigned int fCurrLRUStamp; const GrGLContext& fGL; #ifdef PROGRAM_CACHE_STATS int fTotalRequests; int fCacheMisses; + int fHashMisses; // cache hit but hash table missed #endif }; diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp index 833d811588..b017d5de40 100644 --- a/src/gpu/gl/GrGpuGL_program.cpp +++ b/src/gpu/gl/GrGpuGL_program.cpp @@ -9,12 +9,32 @@ #include "GrEffect.h" #include "GrGLEffect.h" +#include "SkTSearch.h" typedef GrGLUniformManager::UniformHandle UniformHandle; static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle; -#define SKIP_CACHE_CHECK true -#define GR_UINT32_MAX static_cast(-1) +struct GrGpuGL::ProgramCache::Entry { + SK_DECLARE_INST_COUNT_ROOT(Entry); + Entry() : fProgram(NULL), fLRUStamp(0) {} + + SkAutoTUnref fProgram; + unsigned int fLRUStamp; +}; + +SK_DEFINE_INST_COUNT(GrGpuGL::ProgramCache::Entry); + +struct GrGpuGL::ProgramCache::ProgDescLess { + bool operator() (const GrGLProgramDesc& desc, const Entry* entry) { + GrAssert(NULL != entry->fProgram.get()); + return GrGLProgramDesc::Less(desc, entry->fProgram->getDesc()); + } + + bool operator() (const Entry* entry, const GrGLProgramDesc& desc) { + GrAssert(NULL != entry->fProgram.get()); + return GrGLProgramDesc::Less(entry->fProgram->getDesc(), desc); + } +}; GrGpuGL::ProgramCache::ProgramCache(const GrGLContext& gl) : fCount(0) @@ -23,70 +43,147 @@ GrGpuGL::ProgramCache::ProgramCache(const GrGLContext& gl) #ifdef PROGRAM_CACHE_STATS , fTotalRequests(0) , fCacheMisses(0) + , fHashMisses(0) #endif { + for (int i = 0; i < 1 << kHashBits; ++i) { + fHashTable[i] = NULL; + } } GrGpuGL::ProgramCache::~ProgramCache() { + for (int i = 0; i < fCount; ++i){ + SkDELETE(fEntries[i]); + } // dump stats #ifdef PROGRAM_CACHE_STATS SkDebugf("--- Program Cache ---\n"); SkDebugf("Total requests: %d\n", fTotalRequests); SkDebugf("Cache misses: %d\n", fCacheMisses); - SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) - ? (float)fCacheMisses/(float)fTotalRequests : 0.0f); + SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? + 100.f * fCacheMisses / fTotalRequests : + 0.f); + int cacheHits = fTotalRequests - fCacheMisses; + SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f); SkDebugf("---------------------\n"); #endif } void GrGpuGL::ProgramCache::abandon() { for (int i = 0; i < fCount; ++i) { - GrAssert(NULL != fEntries[i].fProgram.get()); - fEntries[i].fProgram->abandon(); - fEntries[i].fProgram.reset(NULL); + GrAssert(NULL != fEntries[i]->fProgram.get()); + fEntries[i]->fProgram->abandon(); + fEntries[i]->fProgram.reset(NULL); } fCount = 0; } +int GrGpuGL::ProgramCache::search(const GrGLProgramDesc& desc) const { + ProgDescLess less; + return SkTSearch(fEntries, fCount, desc, sizeof(Entry*), less); +} + GrGLProgram* GrGpuGL::ProgramCache::getProgram(const GrGLProgramDesc& desc, const GrEffectStage* stages[]) { - Entry newEntry; - newEntry.fKey.setKeyData(desc.asKey()); #ifdef PROGRAM_CACHE_STATS ++fTotalRequests; #endif - Entry* entry = fHashCache.find(newEntry.fKey); + Entry* entry = NULL; + + uint32_t hashIdx = desc.getChecksum(); + hashIdx ^= hashIdx >> 16; + if (kHashBits <= 8) { + hashIdx ^= hashIdx >> 8; + } + hashIdx &=((1 << kHashBits) - 1); + Entry* hashedEntry = fHashTable[hashIdx]; + if (NULL != hashedEntry && hashedEntry->fProgram->getDesc() == desc) { + GrAssert(NULL != hashedEntry->fProgram); + entry = hashedEntry; + } + + int entryIdx; + if (NULL == entry) { + entryIdx = this->search(desc); + if (entryIdx >= 0) { + entry = fEntries[entryIdx]; +#ifdef PROGRAM_CACHE_STATS + ++fHashMisses; +#endif + } + } + if (NULL == entry) { + // We have a cache miss #ifdef PROGRAM_CACHE_STATS ++fCacheMisses; #endif - newEntry.fProgram.reset(GrGLProgram::Create(fGL, desc, stages)); - if (NULL == newEntry.fProgram.get()) { + GrGLProgram* program = GrGLProgram::Create(fGL, desc, stages); + if (NULL == program) { return NULL; } + int purgeIdx = 0; if (fCount < kMaxEntries) { - entry = fEntries + fCount; - ++fCount; + entry = SkNEW(Entry); + purgeIdx = fCount++; + fEntries[purgeIdx] = entry; } else { - GrAssert(kMaxEntries == fCount); - entry = fEntries; + GrAssert(fCount == kMaxEntries); + purgeIdx = 0; for (int i = 1; i < kMaxEntries; ++i) { - if (fEntries[i].fLRUStamp < entry->fLRUStamp) { - entry = fEntries + i; + if (fEntries[i]->fLRUStamp < fEntries[purgeIdx]->fLRUStamp) { + purgeIdx = i; } } - fHashCache.remove(entry->fKey, entry); + entry = fEntries[purgeIdx]; + int purgedHashIdx = entry->fProgram->getDesc().getChecksum() & ((1 << kHashBits) - 1); + if (fHashTable[purgedHashIdx] == entry) { + fHashTable[purgedHashIdx] = NULL; + } } - *entry = newEntry; - fHashCache.insert(entry->fKey, entry); + GrAssert(fEntries[purgeIdx] == entry); + entry->fProgram.reset(program); + // We need to shift fEntries around so that the entry currently at purgeIdx is placed + // just before the entry at ~entryIdx (in order to keep fEntries sorted by descriptor). + entryIdx = ~entryIdx; + if (entryIdx < purgeIdx) { + // Let E and P be the entries at index entryIdx and purgeIdx, respectively. + // If the entries array looks like this: + // aaaaEbbbbbPccccc + // we rearrange it to look like this: + // aaaaPEbbbbbccccc + size_t copySize = (purgeIdx - entryIdx) * sizeof(Entry*); + memmove(fEntries + entryIdx + 1, fEntries + entryIdx, copySize); + fEntries[entryIdx] = entry; + } else if (purgeIdx < entryIdx) { + // If the entries array looks like this: + // aaaaPbbbbbEccccc + // we rearrange it to look like this: + // aaaabbbbbPEccccc + size_t copySize = (entryIdx - purgeIdx - 1) * sizeof(Entry*); + memmove(fEntries + purgeIdx, fEntries + purgeIdx + 1, copySize); + fEntries[entryIdx - 1] = entry; + } +#if GR_DEBUG + GrAssert(NULL != fEntries[0]->fProgram.get()); + for (int i = 0; i < fCount - 1; ++i) { + GrAssert(NULL != fEntries[i + 1]->fProgram.get()); + const GrGLProgramDesc& a = fEntries[i]->fProgram->getDesc(); + const GrGLProgramDesc& b = fEntries[i + 1]->fProgram->getDesc(); + GrAssert(GrGLProgramDesc::Less(a, b)); + GrAssert(!GrGLProgramDesc::Less(b, a)); + } +#endif } + fHashTable[hashIdx] = entry; entry->fLRUStamp = fCurrLRUStamp; - if (GR_UINT32_MAX == fCurrLRUStamp) { + + if (SK_MaxU32 == fCurrLRUStamp) { // wrap around! just trash our LRU, one time hit. for (int i = 0; i < fCount; ++i) { - fEntries[i].fLRUStamp = 0; + fEntries[i]->fLRUStamp = 0; } } ++fCurrLRUStamp; @@ -177,9 +274,6 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC } const GrEffectStage* stages[GrDrawState::kNumStages]; - for (int i = 0; i < GrDrawState::kNumStages; ++i) { - stages[i] = drawState.isStageEnabled(i) ? &drawState.getStage(i) : NULL; - } GrGLProgramDesc desc; GrGLProgramDesc::Build(this->getDrawState(), kDrawPoints_DrawType == type, @@ -188,6 +282,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC dstCoeff, this, dstCopy, + stages, &desc); fCurrentProgram.reset(fProgramCache->getProgram(desc, stages)); @@ -206,19 +301,7 @@ bool GrGpuGL::flushGraphicsState(DrawType type, const GrDeviceCoordTexture* dstC fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff); this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff); - GrColor color; - GrColor coverage; - if (blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag) { - color = 0; - coverage = 0; - } else if (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) { - color = 0xffffffff; - coverage = drawState.getCoverage(); - } else { - color = drawState.getColor(); - coverage = drawState.getCoverage(); - } - fCurrentProgram->setData(this, color, coverage, dstCopy, &fSharedGLProgramState); + fCurrentProgram->setData(this, blendOpts, stages, dstCopy, &fSharedGLProgramState); } this->flushStencil(type); this->flushScissor(); -- cgit v1.2.3