| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2013 Google Inc. | 
 | 3 |  * | 
 | 4 |  * Use of this source code is governed by a BSD-style license that can be | 
 | 5 |  * found in the LICENSE file. | 
 | 6 |  */ | 
 | 7 |  | 
 | 8 | #include "GrGLProgramDesc.h" | 
 | 9 | #include "GrBackendEffectFactory.h" | 
 | 10 | #include "GrDrawEffect.h" | 
 | 11 | #include "GrEffect.h" | 
| bsalomon@google.com | 26e18b5 | 2013-03-29 19:22:36 +0000 | [diff] [blame] | 12 | #include "GrGLShaderBuilder.h" | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 13 | #include "GrGpuGL.h" | 
 | 14 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 15 | #include "SkChecksum.h" | 
 | 16 |  | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 17 | namespace { | 
 | 18 | inline GrGLEffect::EffectKey get_key_and_update_stats(const GrEffectStage& stage, | 
 | 19 |                                                       const GrGLCaps& caps, | 
 | 20 |                                                       bool useExplicitLocalCoords, | 
 | 21 |                                                       bool* setTrueIfReadsDst, | 
 | 22 |                                                       bool* setTrueIfReadsPos) { | 
 | 23 |     const GrEffectRef& effect = *stage.getEffect(); | 
 | 24 |     const GrBackendEffectFactory& factory = effect->getFactory(); | 
 | 25 |     GrDrawEffect drawEffect(stage, useExplicitLocalCoords); | 
 | 26 |     if (effect->willReadDstColor()) { | 
 | 27 |         *setTrueIfReadsDst = true; | 
 | 28 |     } | 
 | 29 |     if (effect->willReadFragmentPosition()) { | 
 | 30 |         *setTrueIfReadsPos = true; | 
 | 31 |     } | 
 | 32 |     return factory.glEffectKey(drawEffect, caps); | 
 | 33 | } | 
 | 34 | } | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 35 | void GrGLProgramDesc::Build(const GrDrawState& drawState, | 
 | 36 |                             bool isPoints, | 
 | 37 |                             GrDrawState::BlendOptFlags blendOpts, | 
 | 38 |                             GrBlendCoeff srcCoeff, | 
 | 39 |                             GrBlendCoeff dstCoeff, | 
 | 40 |                             const GrGpuGL* gpu, | 
| bsalomon@google.com | 26e18b5 | 2013-03-29 19:22:36 +0000 | [diff] [blame] | 41 |                             const GrDeviceCoordTexture* dstCopy, | 
| bsalomon@google.com | 2c84aa3 | 2013-06-06 20:28:57 +0000 | [diff] [blame] | 42 |                             SkTArray<const GrEffectStage*, true>* colorStages, | 
 | 43 |                             SkTArray<const GrEffectStage*, true>* coverageStages, | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 44 |                             GrGLProgramDesc* desc) { | 
| bsalomon@google.com | 2c84aa3 | 2013-06-06 20:28:57 +0000 | [diff] [blame] | 45 |     colorStages->reset(); | 
 | 46 |     coverageStages->reset(); | 
 | 47 |  | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 48 |     // This should already have been caught | 
| tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 49 |     SkASSERT(!(GrDrawState::kSkipDraw_BlendOptFlag & blendOpts)); | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 50 |  | 
 | 51 |     bool skipCoverage = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); | 
 | 52 |  | 
 | 53 |     bool skipColor = SkToBool(blendOpts & (GrDrawState::kEmitTransBlack_BlendOptFlag | | 
 | 54 |                                            GrDrawState::kEmitCoverage_BlendOptFlag)); | 
 | 55 |  | 
 | 56 |     // The descriptor is used as a cache key. Thus when a field of the | 
 | 57 |     // descriptor will not affect program generation (because of the attribute | 
 | 58 |     // bindings in use or other descriptor field settings) it should be set | 
 | 59 |     // to a canonical value to avoid duplicate programs with different keys. | 
 | 60 |  | 
| jvanverth@google.com | 054ae99 | 2013-04-01 20:06:51 +0000 | [diff] [blame] | 61 |     bool requiresColorAttrib = !skipColor && drawState.hasColorVertexAttribute(); | 
 | 62 |     bool requiresCoverageAttrib = !skipCoverage && drawState.hasCoverageVertexAttribute(); | 
 | 63 |     // we only need the local coords if we're actually going to generate effect code | 
 | 64 |     bool requiresLocalCoordAttrib = !(skipCoverage  && skipColor) && | 
 | 65 |                                     drawState.hasLocalCoordAttribute(); | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 66 |  | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 67 |     bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); | 
 | 68 |     bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) || | 
| jvanverth@google.com | 054ae99 | 2013-04-01 20:06:51 +0000 | [diff] [blame] | 69 |                              (!requiresColorAttrib && 0xffffffff == drawState.getColor()); | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 70 |  | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 71 |     int numEffects = (skipColor ? 0 : drawState.numColorStages()) + | 
 | 72 |                      (skipCoverage ? 0 : drawState.numCoverageStages()); | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 73 |  | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 74 |     size_t newKeyLength = KeyLength(numEffects); | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 75 |     bool allocChanged; | 
 | 76 |     desc->fKey.reset(newKeyLength, SkAutoMalloc::kAlloc_OnShrink, &allocChanged); | 
 | 77 |     if (allocChanged || !desc->fInitialized) { | 
 | 78 |         // make sure any padding in the header is zero if we we haven't used this allocation before. | 
 | 79 |         memset(desc->header(), 0, kHeaderSize); | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 80 |     } | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 81 |     // write the key length | 
 | 82 |     *desc->atOffset<uint32_t, kLengthOffset>() = newKeyLength; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 83 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 84 |     KeyHeader* header = desc->header(); | 
 | 85 |     EffectKey* effectKeys = desc->effectKeys(); | 
 | 86 |  | 
 | 87 |     int currEffectKey = 0; | 
| bsalomon@google.com | 26e18b5 | 2013-03-29 19:22:36 +0000 | [diff] [blame] | 88 |     bool readsDst = false; | 
| bsalomon@google.com | b515881 | 2013-05-13 18:50:25 +0000 | [diff] [blame] | 89 |     bool readFragPosition = false; | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 90 |     if (!skipColor) { | 
 | 91 |         for (int s = 0; s < drawState.numColorStages(); ++s) { | 
| skia.committer@gmail.com | 5c493d5 | 2013-06-14 07:00:49 +0000 | [diff] [blame] | 92 |             effectKeys[currEffectKey++] = | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 93 |                 get_key_and_update_stats(drawState.getColorStage(s), gpu->glCaps(), | 
 | 94 |                                          requiresLocalCoordAttrib, &readsDst, &readFragPosition); | 
 | 95 |         } | 
 | 96 |     } | 
 | 97 |     if (!skipCoverage) { | 
 | 98 |         for (int s = 0; s < drawState.numCoverageStages(); ++s) { | 
| skia.committer@gmail.com | 5c493d5 | 2013-06-14 07:00:49 +0000 | [diff] [blame] | 99 |             effectKeys[currEffectKey++] = | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 100 |                 get_key_and_update_stats(drawState.getCoverageStage(s), gpu->glCaps(), | 
 | 101 |                                          requiresLocalCoordAttrib, &readsDst, &readFragPosition); | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 102 |         } | 
 | 103 |     } | 
 | 104 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 105 |     header->fEmitsPointSize = isPoints; | 
 | 106 |     header->fColorFilterXfermode = skipColor ? SkXfermode::kDst_Mode : drawState.getColorFilterMode(); | 
 | 107 |  | 
 | 108 |     // Currently the experimental GS will only work with triangle prims (and it doesn't do anything | 
 | 109 |     // other than pass through values from the VS to the FS anyway). | 
 | 110 | #if GR_GL_EXPERIMENTAL_GS | 
 | 111 | #if 0 | 
 | 112 |     header->fExperimentalGS = gpu->caps().geometryShaderSupport(); | 
 | 113 | #else | 
 | 114 |     header->fExperimentalGS = false; | 
 | 115 | #endif | 
 | 116 | #endif | 
 | 117 |     if (colorIsTransBlack) { | 
 | 118 |         header->fColorInput = kTransBlack_ColorInput; | 
 | 119 |     } else if (colorIsSolidWhite) { | 
 | 120 |         header->fColorInput = kSolidWhite_ColorInput; | 
 | 121 |     } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresColorAttrib) { | 
 | 122 |         header->fColorInput = kUniform_ColorInput; | 
 | 123 |     } else { | 
 | 124 |         header->fColorInput = kAttribute_ColorInput; | 
 | 125 |     } | 
 | 126 |  | 
 | 127 |     bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverage(); | 
 | 128 |  | 
 | 129 |     if (skipCoverage) { | 
 | 130 |         header->fCoverageInput = kTransBlack_ColorInput; | 
 | 131 |     } else if (covIsSolidWhite) { | 
 | 132 |         header->fCoverageInput = kSolidWhite_ColorInput; | 
 | 133 |     } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresCoverageAttrib) { | 
 | 134 |         header->fCoverageInput = kUniform_ColorInput; | 
 | 135 |     } else { | 
 | 136 |         header->fCoverageInput = kAttribute_ColorInput; | 
 | 137 |     } | 
 | 138 |  | 
| bsalomon@google.com | 26e18b5 | 2013-03-29 19:22:36 +0000 | [diff] [blame] | 139 |     if (readsDst) { | 
| tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 140 |         SkASSERT(NULL != dstCopy || gpu->caps()->dstReadInShaderSupport()); | 
| bsalomon@google.com | 6b0cf02 | 2013-05-03 13:35:14 +0000 | [diff] [blame] | 141 |         const GrTexture* dstCopyTexture = NULL; | 
 | 142 |         if (NULL != dstCopy) { | 
 | 143 |             dstCopyTexture = dstCopy->texture(); | 
 | 144 |         } | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 145 |         header->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps()); | 
| tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 146 |         SkASSERT(0 != header->fDstReadKey); | 
| bsalomon@google.com | 26e18b5 | 2013-03-29 19:22:36 +0000 | [diff] [blame] | 147 |     } else { | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 148 |         header->fDstReadKey = 0; | 
| bsalomon@google.com | b515881 | 2013-05-13 18:50:25 +0000 | [diff] [blame] | 149 |     } | 
 | 150 |  | 
 | 151 |     if (readFragPosition) { | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 152 |         header->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(), | 
| bsalomon@google.com | b515881 | 2013-05-13 18:50:25 +0000 | [diff] [blame] | 153 |                                                                       gpu->glCaps()); | 
 | 154 |     } else { | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 155 |         header->fFragPosKey = 0; | 
| bsalomon@google.com | 26e18b5 | 2013-03-29 19:22:36 +0000 | [diff] [blame] | 156 |     } | 
 | 157 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 158 |     // Record attribute indices | 
 | 159 |     header->fPositionAttributeIndex = drawState.positionAttributeIndex(); | 
 | 160 |     header->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex(); | 
| skia.committer@gmail.com | 2d816ad | 2013-05-23 07:01:22 +0000 | [diff] [blame] | 161 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 162 |     // For constant color and coverage we need an attribute with an index beyond those already set | 
 | 163 |     int availableAttributeIndex = drawState.getVertexAttribCount(); | 
 | 164 |     if (requiresColorAttrib) { | 
 | 165 |         header->fColorAttributeIndex = drawState.colorVertexAttributeIndex(); | 
 | 166 |     } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) { | 
| tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 167 |         SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 168 |         header->fColorAttributeIndex = availableAttributeIndex; | 
 | 169 |         availableAttributeIndex++; | 
 | 170 |     } else { | 
 | 171 |         header->fColorAttributeIndex = -1; | 
 | 172 |     } | 
| skia.committer@gmail.com | 2d816ad | 2013-05-23 07:01:22 +0000 | [diff] [blame] | 173 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 174 |     if (requiresCoverageAttrib) { | 
 | 175 |         header->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex(); | 
 | 176 |     } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) { | 
| tfarina@chromium.org | f6de475 | 2013-08-17 00:02:59 +0000 | [diff] [blame] | 177 |         SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 178 |         header->fCoverageAttributeIndex = availableAttributeIndex; | 
 | 179 |     } else { | 
 | 180 |         header->fCoverageAttributeIndex = -1; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 181 |     } | 
 | 182 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 183 |     // Here we deal with whether/how we handle color and coverage separately. | 
| skia.committer@gmail.com | 2d816ad | 2013-05-23 07:01:22 +0000 | [diff] [blame] | 184 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 185 |     // Set these defaults and then possibly change our mind if there is coverage. | 
 | 186 |     header->fDiscardIfZeroCoverage = false; | 
 | 187 |     header->fCoverageOutput = kModulate_CoverageOutput; | 
 | 188 |  | 
 | 189 |     // If we do have coverage determine whether it matters. | 
 | 190 |     bool separateCoverageFromColor = false; | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 191 |     if (!drawState.isCoverageDrawing() && !skipCoverage && | 
 | 192 |         (drawState.numCoverageStages() > 0 || requiresCoverageAttrib)) { | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 193 |         // color filter is applied between color/coverage computation | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 194 |         if (SkXfermode::kDst_Mode != header->fColorFilterXfermode) { | 
 | 195 |             separateCoverageFromColor = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 196 |         } | 
 | 197 |  | 
 | 198 |         // If we're stenciling then we want to discard samples that have zero coverage | 
 | 199 |         if (drawState.getStencil().doesWrite()) { | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 200 |             header->fDiscardIfZeroCoverage = true; | 
 | 201 |             separateCoverageFromColor = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 202 |         } | 
 | 203 |  | 
 | 204 |         if (gpu->caps()->dualSourceBlendingSupport() && | 
 | 205 |             !(blendOpts & (GrDrawState::kEmitCoverage_BlendOptFlag | | 
 | 206 |                            GrDrawState::kCoverageAsAlpha_BlendOptFlag))) { | 
 | 207 |             if (kZero_GrBlendCoeff == dstCoeff) { | 
 | 208 |                 // write the coverage value to second color | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 209 |                 header->fCoverageOutput =  kSecondaryCoverage_CoverageOutput; | 
 | 210 |                 separateCoverageFromColor = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 211 |             } else if (kSA_GrBlendCoeff == dstCoeff) { | 
 | 212 |                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 213 |                 header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput; | 
 | 214 |                 separateCoverageFromColor = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 215 |             } else if (kSC_GrBlendCoeff == dstCoeff) { | 
 | 216 |                 // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 217 |                 header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput; | 
 | 218 |                 separateCoverageFromColor = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 219 |             } | 
| bsalomon@google.com | 5920ac2 | 2013-04-19 13:14:45 +0000 | [diff] [blame] | 220 |         } else if (readsDst && | 
| bsalomon@google.com | 0c89db2 | 2013-05-15 17:53:04 +0000 | [diff] [blame] | 221 |                    kOne_GrBlendCoeff == srcCoeff && | 
 | 222 |                    kZero_GrBlendCoeff == dstCoeff) { | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 223 |             header->fCoverageOutput = kCombineWithDst_CoverageOutput; | 
 | 224 |             separateCoverageFromColor = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 225 |         } | 
 | 226 |     } | 
| bsalomon@google.com | eb6879f | 2013-06-13 19:34:18 +0000 | [diff] [blame] | 227 |     if (!skipColor) { | 
 | 228 |         for (int s = 0; s < drawState.numColorStages(); ++s) { | 
 | 229 |             colorStages->push_back(&drawState.getColorStage(s)); | 
 | 230 |         } | 
 | 231 |         header->fColorEffectCnt = drawState.numColorStages(); | 
 | 232 |     } | 
 | 233 |     if (!skipCoverage) { | 
 | 234 |         SkTArray<const GrEffectStage*, true>* array; | 
 | 235 |         if (separateCoverageFromColor) { | 
 | 236 |             array = coverageStages; | 
 | 237 |             header->fCoverageEffectCnt = drawState.numCoverageStages(); | 
 | 238 |         } else { | 
 | 239 |             array = colorStages; | 
 | 240 |             header->fColorEffectCnt += drawState.numCoverageStages(); | 
 | 241 |         } | 
 | 242 |         for (int s = 0; s < drawState.numCoverageStages(); ++s) { | 
 | 243 |             array->push_back(&drawState.getCoverageStage(s)); | 
 | 244 |         } | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 245 |     } | 
 | 246 |  | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 247 |     *desc->checksum() = 0; | 
 | 248 |     *desc->checksum() = SkChecksum::Compute(reinterpret_cast<uint32_t*>(desc->fKey.get()), | 
 | 249 |                                             newKeyLength); | 
 | 250 |     desc->fInitialized = true; | 
| bsalomon@google.com | 798c8c4 | 2013-03-27 19:50:27 +0000 | [diff] [blame] | 251 | } | 
| bsalomon@google.com | 2db3ded | 2013-05-22 14:34:04 +0000 | [diff] [blame] | 252 |  | 
 | 253 | GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) { | 
 | 254 |     fInitialized = other.fInitialized; | 
 | 255 |     if (fInitialized) { | 
 | 256 |         size_t keyLength = other.keyLength(); | 
 | 257 |         fKey.reset(keyLength); | 
 | 258 |         memcpy(fKey.get(), other.fKey.get(), keyLength); | 
 | 259 |     } | 
 | 260 |     return *this; | 
 | 261 | } |