/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrGpuGL.h" #include "GrCustomStage.h" #include "GrGLProgramStage.h" #include "GrGpuVertex.h" #define SKIP_CACHE_CHECK true #define GR_UINT32_MAX static_cast(-1) void GrGpuGL::ProgramCache::Entry::copyAndTakeOwnership(Entry& entry) { fProgramData.copyAndTakeOwnership(entry.fProgramData); fKey = entry.fKey; // ownership transfer fLRUStamp = entry.fLRUStamp; } GrGpuGL::ProgramCache::ProgramCache(const GrGLContextInfo& gl) : fCount(0) , fCurrLRUStamp(0) , fGL(gl) { } GrGpuGL::ProgramCache::~ProgramCache() { for (int i = 0; i < fCount; ++i) { GrGpuGL::DeleteProgram(fGL.interface(), &fEntries[i].fProgramData); } } void GrGpuGL::ProgramCache::abandon() { fCount = 0; } GrGLProgram::CachedData* GrGpuGL::ProgramCache::getProgramData( const GrGLProgram& desc, GrCustomStage** stages) { Entry newEntry; newEntry.fKey.setKeyData(desc.keyData()); Entry* entry = fHashCache.find(newEntry.fKey); if (NULL == entry) { if (!desc.genProgram(fGL, stages, &newEntry.fProgramData)) { return NULL; } if (fCount < kMaxEntries) { entry = fEntries + fCount; ++fCount; } else { GrAssert(kMaxEntries == fCount); entry = fEntries; for (int i = 1; i < kMaxEntries; ++i) { if (fEntries[i].fLRUStamp < entry->fLRUStamp) { entry = fEntries + i; } } fHashCache.remove(entry->fKey, entry); GrGpuGL::DeleteProgram(fGL.interface(), &entry->fProgramData); } entry->copyAndTakeOwnership(newEntry); fHashCache.insert(entry->fKey, entry); } entry->fLRUStamp = fCurrLRUStamp; if (GR_UINT32_MAX == fCurrLRUStamp) { // wrap around! just trash our LRU, one time hit. for (int i = 0; i < fCount; ++i) { fEntries[i].fLRUStamp = 0; } } ++fCurrLRUStamp; return &entry->fProgramData; } void GrGpuGL::DeleteProgram(const GrGLInterface* gl, CachedData* programData) { GR_GL_CALL(gl, DeleteShader(programData->fVShaderID)); if (programData->fGShaderID) { GR_GL_CALL(gl, DeleteShader(programData->fGShaderID)); } GR_GL_CALL(gl, DeleteShader(programData->fFShaderID)); GR_GL_CALL(gl, DeleteProgram(programData->fProgramID)); GR_DEBUGCODE(memset(programData, 0, sizeof(*programData));) } //////////////////////////////////////////////////////////////////////////////// void GrGpuGL::abandonResources(){ INHERITED::abandonResources(); fProgramCache->abandon(); fHWProgramID = 0; } //////////////////////////////////////////////////////////////////////////////// #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X) void GrGpuGL::flushViewMatrix() { const GrGLRenderTarget* rt = static_cast(this->getDrawState().getRenderTarget()); SkISize viewportSize; const GrGLIRect& viewport = rt->getViewport(); viewportSize.set(viewport.fWidth, viewport.fHeight); const GrMatrix& vm = this->getDrawState().getViewMatrix(); if (!fProgramData->fViewMatrix.cheapEqualTo(vm) || fProgramData->fViewportSize != viewportSize) { GrMatrix m; m.setAll( GrIntToScalar(2) / viewportSize.fWidth, 0, -GR_Scalar1, 0,-GrIntToScalar(2) / viewportSize.fHeight, GR_Scalar1, 0, 0, GrMatrix::I()[8]); m.setConcat(m, vm); // ES doesn't allow you to pass true to the transpose param, // so do our own transpose GrGLfloat mt[] = { GrScalarToFloat(m[GrMatrix::kMScaleX]), GrScalarToFloat(m[GrMatrix::kMSkewY]), GrScalarToFloat(m[GrMatrix::kMPersp0]), GrScalarToFloat(m[GrMatrix::kMSkewX]), GrScalarToFloat(m[GrMatrix::kMScaleY]), GrScalarToFloat(m[GrMatrix::kMPersp1]), GrScalarToFloat(m[GrMatrix::kMTransX]), GrScalarToFloat(m[GrMatrix::kMTransY]), GrScalarToFloat(m[GrMatrix::kMPersp2]) }; GrAssert(GrGLProgram::kUnusedUniform != fProgramData->fUniLocations.fViewMatrixUni); GL_CALL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni, 1, false, mt)); fProgramData->fViewMatrix = vm; fProgramData->fViewportSize = viewportSize; } } /////////////////////////////////////////////////////////////////////////////// // helpers for texture matrices void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture, GrMatrix* matrix) { GrAssert(NULL != texture); GrAssert(NULL != matrix); GrGLTexture::Orientation orientation = texture->orientation(); if (GrGLTexture::kBottomUp_Orientation == orientation) { GrMatrix invY; invY.setAll(GR_Scalar1, 0, 0, 0, -GR_Scalar1, GR_Scalar1, 0, 0, GrMatrix::I()[8]); matrix->postConcat(invY); } else { GrAssert(GrGLTexture::kTopDown_Orientation == orientation); } } bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture, const GrSamplerState& sampler) { GrAssert(NULL != texture); if (!sampler.getMatrix().isIdentity()) { return false; } GrGLTexture::Orientation orientation = texture->orientation(); if (GrGLTexture::kBottomUp_Orientation == orientation) { return false; } else { GrAssert(GrGLTexture::kTopDown_Orientation == orientation); } return true; } /////////////////////////////////////////////////////////////////////////////// void GrGpuGL::flushTextureMatrixAndDomain(int s) { const GrDrawState& drawState = this->getDrawState(); const GrGLTexture* texture = static_cast(drawState.getTexture(s)); if (NULL != texture) { bool orientationChange = fProgramData->fTextureOrientation[s] != texture->orientation(); const GrGLint& matrixUni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni; const GrMatrix& hwMatrix = fProgramData->fTextureMatrices[s]; const GrMatrix& samplerMatrix = drawState.getSampler(s).getMatrix(); if (GrGLProgram::kUnusedUniform != matrixUni && (orientationChange || !hwMatrix.cheapEqualTo(samplerMatrix))) { GrMatrix m = samplerMatrix; AdjustTextureMatrix(texture, &m); // ES doesn't allow you to pass true to the transpose param, // so do our own transpose GrGLfloat mt[] = { GrScalarToFloat(m[GrMatrix::kMScaleX]), GrScalarToFloat(m[GrMatrix::kMSkewY]), GrScalarToFloat(m[GrMatrix::kMPersp0]), GrScalarToFloat(m[GrMatrix::kMSkewX]), GrScalarToFloat(m[GrMatrix::kMScaleY]), GrScalarToFloat(m[GrMatrix::kMPersp1]), GrScalarToFloat(m[GrMatrix::kMTransX]), GrScalarToFloat(m[GrMatrix::kMTransY]), GrScalarToFloat(m[GrMatrix::kMPersp2]) }; GL_CALL(UniformMatrix3fv(matrixUni, 1, false, mt)); fProgramData->fTextureMatrices[s] = samplerMatrix; } const GrGLint& domUni = fProgramData->fUniLocations.fStages[s].fTexDomUni; const GrRect &texDom = drawState.getSampler(s).getTextureDomain(); if (GrGLProgram::kUnusedUniform != domUni && (orientationChange ||fProgramData->fTextureDomain[s] != texDom)) { fProgramData->fTextureDomain[s] = texDom; float values[4] = { GrScalarToFloat(texDom.left()), GrScalarToFloat(texDom.top()), GrScalarToFloat(texDom.right()), GrScalarToFloat(texDom.bottom()) }; // vertical flip if necessary if (GrGLTexture::kBottomUp_Orientation == texture->orientation()) { values[1] = 1.0f - values[1]; values[3] = 1.0f - values[3]; // The top and bottom were just flipped, so correct the ordering // of elements so that values = (l, t, r, b). SkTSwap(values[1], values[3]); } GL_CALL(Uniform4fv(domUni, 1, values)); } fProgramData->fTextureOrientation[s] = texture->orientation(); } } void GrGpuGL::flushColorMatrix() { // const ProgramDesc& desc = fCurrentProgram.getDesc(); int matrixUni = fProgramData->fUniLocations.fColorMatrixUni; int vecUni = fProgramData->fUniLocations.fColorMatrixVecUni; if (GrGLProgram::kUnusedUniform != matrixUni && GrGLProgram::kUnusedUniform != vecUni) { const float* m = this->getDrawState().getColorMatrix(); GrGLfloat mt[] = { m[0], m[5], m[10], m[15], m[1], m[6], m[11], m[16], m[2], m[7], m[12], m[17], m[3], m[8], m[13], m[18], }; static float scale = 1.0f / 255.0f; GrGLfloat vec[] = { m[4] * scale, m[9] * scale, m[14] * scale, m[19] * scale, }; GL_CALL(UniformMatrix4fv(matrixUni, 1, false, mt)); GL_CALL(Uniform4fv(vecUni, 1, vec)); } } static const float ONE_OVER_255 = 1.f / 255.f; #define GR_COLOR_TO_VEC4(color) {\ GrColorUnpackR(color) * ONE_OVER_255,\ GrColorUnpackG(color) * ONE_OVER_255,\ GrColorUnpackB(color) * ONE_OVER_255,\ GrColorUnpackA(color) * ONE_OVER_255 \ } void GrGpuGL::flushColor(GrColor color) { const ProgramDesc& desc = fCurrentProgram.getDesc(); const GrDrawState& drawState = this->getDrawState(); if (this->getVertexLayout() & kColor_VertexLayoutBit) { // color will be specified per-vertex as an attribute // invalidate the const vertex attrib color fHWConstAttribColor = GrColor_ILLEGAL; } else { switch (desc.fColorInput) { case ProgramDesc::kAttribute_ColorInput: if (fHWConstAttribColor != color) { // OpenGL ES only supports the float varieties of // glVertexAttrib float c[] = GR_COLOR_TO_VEC4(color); GL_CALL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(), c)); fHWConstAttribColor = color; } break; case ProgramDesc::kUniform_ColorInput: if (fProgramData->fColor != color) { // OpenGL ES doesn't support unsigned byte varieties of // glUniform float c[] = GR_COLOR_TO_VEC4(color); GrAssert(GrGLProgram::kUnusedUniform != fProgramData->fUniLocations.fColorUni); GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorUni, 1, c)); fProgramData->fColor = color; } break; case ProgramDesc::kSolidWhite_ColorInput: case ProgramDesc::kTransBlack_ColorInput: break; default: GrCrash("Unknown color type."); } } if (fProgramData->fUniLocations.fColorFilterUni != GrGLProgram::kUnusedUniform && fProgramData->fColorFilterColor != drawState.getColorFilterColor()) { float c[] = GR_COLOR_TO_VEC4(drawState.getColorFilterColor()); GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c)); fProgramData->fColorFilterColor = drawState.getColorFilterColor(); } } void GrGpuGL::flushCoverage(GrColor coverage) { const ProgramDesc& desc = fCurrentProgram.getDesc(); // const GrDrawState& drawState = this->getDrawState(); if (this->getVertexLayout() & kCoverage_VertexLayoutBit) { // coverage will be specified per-vertex as an attribute // invalidate the const vertex attrib coverage fHWConstAttribCoverage = GrColor_ILLEGAL; } else { switch (desc.fCoverageInput) { case ProgramDesc::kAttribute_ColorInput: if (fHWConstAttribCoverage != coverage) { // OpenGL ES only supports the float varieties of // glVertexAttrib float c[] = GR_COLOR_TO_VEC4(coverage); GL_CALL(VertexAttrib4fv(GrGLProgram::CoverageAttributeIdx(), c)); fHWConstAttribCoverage = coverage; } break; case ProgramDesc::kUniform_ColorInput: if (fProgramData->fCoverage != coverage) { // OpenGL ES doesn't support unsigned byte varieties of // glUniform float c[] = GR_COLOR_TO_VEC4(coverage); GrAssert(GrGLProgram::kUnusedUniform != fProgramData->fUniLocations.fCoverageUni); GL_CALL(Uniform4fv(fProgramData->fUniLocations.fCoverageUni, 1, c)); fProgramData->fCoverage = coverage; } break; case ProgramDesc::kSolidWhite_ColorInput: case ProgramDesc::kTransBlack_ColorInput: break; default: GrCrash("Unknown coverage type."); } } } bool GrGpuGL::flushGraphicsState(GrPrimitiveType type) { const GrDrawState& drawState = this->getDrawState(); // GrGpu::setupClipAndFlushState should have already checked this // and bailed if not true. GrAssert(NULL != drawState.getRenderTarget()); this->flushStencil(); this->flushAAState(type); GrBlendCoeff srcCoeff; GrBlendCoeff dstCoeff; BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff); if (kSkipDraw_BlendOptFlag & blendOpts) { return false; } GrCustomStage* customStages [GrDrawState::kNumStages]; this->buildProgram(type, blendOpts, dstCoeff, customStages); fProgramData = fProgramCache->getProgramData(fCurrentProgram, customStages); if (NULL == fProgramData) { GrAssert(!"Failed to create program!"); return false; } if (fHWProgramID != fProgramData->fProgramID) { GL_CALL(UseProgram(fProgramData->fProgramID)); fHWProgramID = fProgramData->fProgramID; } fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff); this->flushBlend(type, srcCoeff, dstCoeff); GrColor color; GrColor coverage; if (blendOpts & kEmitTransBlack_BlendOptFlag) { color = 0; coverage = 0; } else if (blendOpts & kEmitCoverage_BlendOptFlag) { color = 0xffffffff; coverage = drawState.getCoverage(); } else { color = drawState.getColor(); coverage = drawState.getCoverage(); } this->flushColor(color); this->flushCoverage(coverage); this->flushViewMatrix(); for (int s = 0; s < GrDrawState::kNumStages; ++s) { if (this->isStageEnabled(s)) { #if GR_DEBUG // check for circular rendering GrAssert(NULL == drawState.getRenderTarget() || NULL == drawState.getTexture(s) || drawState.getTexture(s)->asRenderTarget() != drawState.getRenderTarget()); #endif this->flushBoundTextureAndParams(s); this->flushTextureMatrixAndDomain(s); if (NULL != fProgramData->fCustomStage[s]) { const GrSamplerState& sampler = this->getDrawState().getSampler(s); const GrGLTexture* texture = static_cast( this->getDrawState().getTexture(s)); fProgramData->fCustomStage[s]->setData( this->glInterface(), *texture, *sampler.getCustomStage(), s); } } } this->flushColorMatrix(); GrIRect* rect = NULL; GrIRect clipBounds; if (drawState.isClipState() && fClip.hasConservativeBounds()) { fClip.getConservativeBounds().roundOut(&clipBounds); rect = &clipBounds; } // This must come after textures are flushed because a texture may need // to be msaa-resolved (which will modify bound FBO state). this->flushRenderTarget(rect); return true; } #if GR_TEXT_SCALAR_IS_USHORT #define TEXT_COORDS_GL_TYPE GR_GL_UNSIGNED_SHORT #define TEXT_COORDS_ARE_NORMALIZED 1 #elif GR_TEXT_SCALAR_IS_FLOAT #define TEXT_COORDS_GL_TYPE GR_GL_FLOAT #define TEXT_COORDS_ARE_NORMALIZED 0 #elif GR_TEXT_SCALAR_IS_FIXED #define TEXT_COORDS_GL_TYPE GR_GL_FIXED #define TEXT_COORDS_ARE_NORMALIZED 0 #else #error "unknown GR_TEXT_SCALAR type" #endif void GrGpuGL::setupGeometry(int* startVertex, int* startIndex, int vertexCount, int indexCount) { int newColorOffset; int newCoverageOffset; int newTexCoordOffsets[GrDrawState::kMaxTexCoords]; int newEdgeOffset; GrVertexLayout currLayout = this->getVertexLayout(); GrGLsizei newStride = VertexSizeAndOffsetsByIdx( currLayout, newTexCoordOffsets, &newColorOffset, &newCoverageOffset, &newEdgeOffset); int oldColorOffset; int oldCoverageOffset; int oldTexCoordOffsets[GrDrawState::kMaxTexCoords]; int oldEdgeOffset; GrGLsizei oldStride = VertexSizeAndOffsetsByIdx( fHWGeometryState.fVertexLayout, oldTexCoordOffsets, &oldColorOffset, &oldCoverageOffset, &oldEdgeOffset); bool indexed = NULL != startIndex; int extraVertexOffset; int extraIndexOffset; this->setBuffers(indexed, &extraVertexOffset, &extraIndexOffset); GrGLenum scalarType; bool texCoordNorm; if (currLayout & kTextFormat_VertexLayoutBit) { scalarType = TEXT_COORDS_GL_TYPE; texCoordNorm = SkToBool(TEXT_COORDS_ARE_NORMALIZED); } else { GR_STATIC_ASSERT(GR_SCALAR_IS_FLOAT); scalarType = GR_GL_FLOAT; texCoordNorm = false; } size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride; *startVertex = 0; if (indexed) { *startIndex += extraIndexOffset; } // all the Pointers must be set if any of these are true bool allOffsetsChange = fHWGeometryState.fArrayPtrsDirty || vertexOffset != fHWGeometryState.fVertexOffset || newStride != oldStride; // position and tex coord offsets change if above conditions are true // or the type/normalization changed based on text vs nontext type coords. bool posAndTexChange = allOffsetsChange || (((TEXT_COORDS_GL_TYPE != GR_GL_FLOAT) || TEXT_COORDS_ARE_NORMALIZED) && (kTextFormat_VertexLayoutBit & (fHWGeometryState.fVertexLayout ^ currLayout))); if (posAndTexChange) { int idx = GrGLProgram::PositionAttributeIdx(); GL_CALL(VertexAttribPointer(idx, 2, scalarType, false, newStride, (GrGLvoid*)vertexOffset)); fHWGeometryState.fVertexOffset = vertexOffset; } for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) { if (newTexCoordOffsets[t] > 0) { GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]); int idx = GrGLProgram::TexCoordAttributeIdx(t); if (oldTexCoordOffsets[t] <= 0) { GL_CALL(EnableVertexAttribArray(idx)); GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, newStride, texCoordOffset)); } else if (posAndTexChange || newTexCoordOffsets[t] != oldTexCoordOffsets[t]) { GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, newStride, texCoordOffset)); } } else if (oldTexCoordOffsets[t] > 0) { GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t))); } } if (newColorOffset > 0) { GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset); int idx = GrGLProgram::ColorAttributeIdx(); if (oldColorOffset <= 0) { GL_CALL(EnableVertexAttribArray(idx)); GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE, true, newStride, colorOffset)); } else if (allOffsetsChange || newColorOffset != oldColorOffset) { GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE, true, newStride, colorOffset)); } } else if (oldColorOffset > 0) { GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx())); } if (newCoverageOffset > 0) { GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset); int idx = GrGLProgram::CoverageAttributeIdx(); if (oldCoverageOffset <= 0) { GL_CALL(EnableVertexAttribArray(idx)); GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE, true, newStride, coverageOffset)); } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) { GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE, true, newStride, coverageOffset)); } } else if (oldCoverageOffset > 0) { GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx())); } if (newEdgeOffset > 0) { GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset); int idx = GrGLProgram::EdgeAttributeIdx(); if (oldEdgeOffset <= 0) { GL_CALL(EnableVertexAttribArray(idx)); GL_CALL(VertexAttribPointer(idx, 4, scalarType, false, newStride, edgeOffset)); } else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) { GL_CALL(VertexAttribPointer(idx, 4, scalarType, false, newStride, edgeOffset)); } } else if (oldEdgeOffset > 0) { GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx())); } fHWGeometryState.fVertexLayout = currLayout; fHWGeometryState.fArrayPtrsDirty = false; } namespace { void setup_custom_stage(GrGLProgram::ProgramDesc::StageDesc* stage, const GrSamplerState& sampler, GrCustomStage** customStages, GrGLProgram* program, int index) { GrCustomStage* customStage = sampler.getCustomStage(); if (customStage) { const GrProgramStageFactory& factory = customStage->getFactory(); stage->fCustomStageKey = factory.glStageKey(*customStage); customStages[index] = customStage; } else { stage->fCustomStageKey = 0; customStages[index] = NULL; } } } void GrGpuGL::buildProgram(GrPrimitiveType type, BlendOptFlags blendOpts, GrBlendCoeff dstCoeff, GrCustomStage** customStages) { ProgramDesc& desc = fCurrentProgram.fProgramDesc; const GrDrawState& drawState = this->getDrawState(); // This should already have been caught GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts)); bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag); bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag | kEmitCoverage_BlendOptFlag)); // The descriptor is used as a cache key. Thus when a field of the // descriptor will not affect program generation (because of the vertex // layout in use or other descriptor field settings) it should be set // to a canonical value to avoid duplicate programs with different keys. // Must initialize all fields or cache will have false negatives! desc.fVertexLayout = this->getVertexLayout(); desc.fEmitsPointSize = kPoints_PrimitiveType == type; bool requiresAttributeColors = !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit); bool requiresAttributeCoverage = !skipCoverage && SkToBool(desc.fVertexLayout & kCoverage_VertexLayoutBit); // fColorInput/fCoverageInput records how colors are specified for the. // program. So we strip the bits from the layout to avoid false negatives // when searching for an existing program in the cache. desc.fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit); desc.fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode(); desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit); // no reason to do edge aa or look at per-vertex coverage if coverage is // ignored if (skipCoverage) { desc.fVertexLayout &= ~(kEdge_VertexLayoutBit | kCoverage_VertexLayoutBit); } bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag); bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) || (!requiresAttributeColors && 0xffffffff == drawState.getColor()); if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) { desc.fColorInput = ProgramDesc::kTransBlack_ColorInput; } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) { desc.fColorInput = ProgramDesc::kSolidWhite_ColorInput; } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) { desc.fColorInput = ProgramDesc::kUniform_ColorInput; } else { desc.fColorInput = ProgramDesc::kAttribute_ColorInput; } bool covIsSolidWhite = !requiresAttributeCoverage && 0xffffffff == drawState.getCoverage(); if (skipCoverage) { desc.fCoverageInput = ProgramDesc::kTransBlack_ColorInput; } else if (covIsSolidWhite) { desc.fCoverageInput = ProgramDesc::kSolidWhite_ColorInput; } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) { desc.fCoverageInput = ProgramDesc::kUniform_ColorInput; } else { desc.fCoverageInput = ProgramDesc::kAttribute_ColorInput; } int lastEnabledStage = -1; if (!skipCoverage && (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit)) { desc.fVertexEdgeType = drawState.getVertexEdgeType(); } else { // use canonical value when not set to avoid cache misses desc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType; } for (int s = 0; s < GrDrawState::kNumStages; ++s) { StageDesc& stage = desc.fStages[s]; stage.fOptFlags = 0; stage.setEnabled(this->isStageEnabled(s)); bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage; if (!skip && stage.isEnabled()) { lastEnabledStage = s; const GrGLTexture* texture = static_cast(drawState.getTexture(s)); GrAssert(NULL != texture); const GrSamplerState& sampler = drawState.getSampler(s); // we matrix to invert when orientation is TopDown, so make sure // we aren't in that case before flagging as identity. if (TextureMatrixIsIdentity(texture, sampler)) { stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit; } else if (!sampler.getMatrix().hasPerspective()) { stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit; } if (sampler.hasTextureDomain()) { GrAssert(GrSamplerState::kClamp_WrapMode == sampler.getWrapX() && GrSamplerState::kClamp_WrapMode == sampler.getWrapY()); stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit; } stage.fInConfigFlags = 0; if (!this->glCaps().textureSwizzleSupport()) { if (GrPixelConfigIsAlphaOnly(texture->config())) { // if we don't have texture swizzle support then // the shader must smear the single channel after // reading the texture if (this->glCaps().textureRedSupport()) { // we can use R8 textures so use kSmearRed stage.fInConfigFlags |= StageDesc::kSmearRed_InConfigFlag; } else { // we can use A8 textures so use kSmearAlpha stage.fInConfigFlags |= StageDesc::kSmearAlpha_InConfigFlag; } } else if (sampler.swapsRAndB()) { stage.fInConfigFlags |= StageDesc::kSwapRAndB_InConfigFlag; } } if (GrPixelConfigIsUnpremultiplied(texture->config())) { // The shader generator assumes that color channels are bytes // when rounding. GrAssert(4 == GrBytesPerPixel(texture->config())); if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) { stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag; } else { stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag; } } setup_custom_stage(&stage, sampler, customStages, &fCurrentProgram, s); } else { stage.fOptFlags = 0; stage.fInConfigFlags = 0; stage.fCustomStageKey = 0; customStages[s] = NULL; } } if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) { // The shader generator assumes that color channels are bytes // when rounding. GrAssert(4 == GrBytesPerPixel(drawState.getRenderTarget()->config())); if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) { desc.fOutputConfig = ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig; } else { desc.fOutputConfig = ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig; } } else { desc.fOutputConfig = ProgramDesc::kPremultiplied_OutputConfig; } desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput; // 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 0 && GR_GL_EXPERIMENTAL_GS desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport; #endif // we want to avoid generating programs with different "first cov stage" // values when they would compute the same result. // We set field in the desc to kNumStages when either there are no // coverage stages or the distinction between coverage and color is // immaterial. int firstCoverageStage = GrDrawState::kNumStages; desc.fFirstCoverageStage = GrDrawState::kNumStages; bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage; if (hasCoverage) { firstCoverageStage = drawState.getFirstCoverageStage(); } // other coverage inputs if (!hasCoverage) { hasCoverage = requiresAttributeCoverage || (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit); } if (hasCoverage) { // color filter is applied between color/coverage computation if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) { desc.fFirstCoverageStage = firstCoverageStage; } if (this->getCaps().fDualSourceBlendingSupport && !(blendOpts & (kEmitCoverage_BlendOptFlag | kCoverageAsAlpha_BlendOptFlag))) { if (kZero_BlendCoeff == dstCoeff) { // write the coverage value to second color desc.fDualSrcOutput = ProgramDesc::kCoverage_DualSrcOutput; desc.fFirstCoverageStage = firstCoverageStage; } else if (kSA_BlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially // cover desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput; desc.fFirstCoverageStage = firstCoverageStage; } else if (kSC_BlendCoeff == dstCoeff) { // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially // cover desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput; desc.fFirstCoverageStage = firstCoverageStage; } } } }