| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  * Copyright 2014 Google Inc. |    2  * Copyright 2014 Google Inc. | 
|    3  * |    3  * | 
|    4  * Use of this source code is governed by a BSD-style license that can be |    4  * Use of this source code is governed by a BSD-style license that can be | 
|    5  * found in the LICENSE file. |    5  * found in the LICENSE file. | 
|    6  */ |    6  */ | 
|    7  |    7  | 
|    8 #include "GrGLProgramBuilder.h" |    8 #include "GrGLProgramBuilder.h" | 
|    9  |    9  | 
|   10 #include "GrAutoLocaleSetter.h" |   10 #include "GrAutoLocaleSetter.h" | 
|   11 #include "GrCoordTransform.h" |   11 #include "GrCoordTransform.h" | 
|   12 #include "GrGLProgramBuilder.h" |   12 #include "GrGLProgramBuilder.h" | 
|   13 #include "GrTexture.h" |   13 #include "GrTexture.h" | 
|   14 #include "SkRTConf.h" |   14 #include "SkRTConf.h" | 
|   15 #include "SkTraceEvent.h" |   15 #include "SkTraceEvent.h" | 
|   16 #include "gl/GrGLFragmentProcessor.h" |   16 #include "gl/GrGLFragmentProcessor.h" | 
|   17 #include "gl/GrGLGeometryProcessor.h" |   17 #include "gl/GrGLGeometryProcessor.h" | 
|   18 #include "gl/GrGLGpu.h" |   18 #include "gl/GrGLGpu.h" | 
|   19 #include "gl/GrGLProgram.h" |   19 #include "gl/GrGLProgram.h" | 
|   20 #include "gl/GrGLSLPrettyPrint.h" |   20 #include "gl/GrGLSLPrettyPrint.h" | 
|   21 #include "gl/GrGLXferProcessor.h" |   21 #include "gl/GrGLXferProcessor.h" | 
|   22 #include "gl/builders/GrGLShaderStringBuilder.h" |   22 #include "gl/builders/GrGLShaderStringBuilder.h" | 
|   23 #include "glsl/GrGLSLCaps.h" |   23 #include "glsl/GrGLSLCaps.h" | 
|   24 #include "glsl/GrGLSLProgramDataManager.h" |   24 #include "glsl/GrGLSLProgramDataManager.h" | 
|   25 #include "glsl/GrGLSLTextureSampler.h" |   25 #include "glsl/GrGLSLTextureSampler.h" | 
|   26  |   26  | 
|   27 #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X) |   27 #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X) | 
|   28 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X) |   28 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X) | 
|   29  |   29  | 
|   30 const int GrGLProgramBuilder::kVarsPerBlock = 8; |  | 
|   31  |  | 
|   32 GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gp
     u) { |   30 GrGLProgram* GrGLProgramBuilder::CreateProgram(const DrawArgs& args, GrGLGpu* gp
     u) { | 
|   33     GrAutoLocaleSetter als("C"); |   31     GrAutoLocaleSetter als("C"); | 
|   34  |   32  | 
|   35     // create a builder.  This will be handed off to effects so they can use it 
     to add |   33     // create a builder.  This will be handed off to effects so they can use it 
     to add | 
|   36     // uniforms, varyings, textures, etc |   34     // uniforms, varyings, textures, etc | 
|   37     SkAutoTDelete<GrGLProgramBuilder> builder(new GrGLProgramBuilder(gpu, args))
     ; |   35     SkAutoTDelete<GrGLProgramBuilder> builder(new GrGLProgramBuilder(gpu, args))
     ; | 
|   38  |   36  | 
|   39     GrGLProgramBuilder* pb = builder.get(); |   37     GrGLProgramBuilder* pb = builder.get(); | 
|   40  |   38  | 
|   41     // TODO: Once all stages can handle taking a float or vec4 and correctly han
     dling them we can |   39     // TODO: Once all stages can handle taking a float or vec4 and correctly han
     dling them we can | 
|   42     // seed correctly here |   40     // seed correctly here | 
|   43     GrGLSLExpr4 inputColor; |   41     GrGLSLExpr4 inputColor; | 
|   44     GrGLSLExpr4 inputCoverage; |   42     GrGLSLExpr4 inputCoverage; | 
|   45  |   43  | 
|   46     if (!pb->emitAndInstallProcs(&inputColor, &inputCoverage)) { |   44     if (!pb->emitAndInstallProcs(&inputColor, &inputCoverage)) { | 
|   47         return nullptr; |   45         return nullptr; | 
|   48     } |   46     } | 
|   49  |   47  | 
|   50     return pb->finalize(); |   48     return pb->finalize(); | 
|   51 } |   49 } | 
|   52  |   50  | 
|   53 ///////////////////////////////////////////////////////////////////////////// |   51 ///////////////////////////////////////////////////////////////////////////// | 
|   54  |   52  | 
|   55 GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu, const DrawArgs& args) |   53 GrGLProgramBuilder::GrGLProgramBuilder(GrGLGpu* gpu, const DrawArgs& args) | 
|   56     : fVS(this) |   54     : INHERITED(args) | 
|   57     , fGS(this) |  | 
|   58     , fFS(this, args.fDesc->header().fFragPosKey) |  | 
|   59     , fStageIndex(-1) |  | 
|   60     , fGeometryProcessor(nullptr) |   55     , fGeometryProcessor(nullptr) | 
|   61     , fXferProcessor(nullptr) |   56     , fXferProcessor(nullptr) | 
|   62     , fArgs(args) |  | 
|   63     , fGpu(gpu) |   57     , fGpu(gpu) | 
|   64     , fUniforms(kVarsPerBlock) |   58     , fUniforms(kVarsPerBlock) | 
|   65     , fSamplerUniforms(4) |   59     , fSamplerUniforms(4) | 
|   66     , fSeparableVaryingInfos(kVarsPerBlock) { |   60     , fSeparableVaryingInfos(kVarsPerBlock) { | 
|   67 } |   61 } | 
|   68  |   62  | 
|   69 void GrGLProgramBuilder::addVarying(const char* name, |   63 void GrGLProgramBuilder::addVarying(const char* name, | 
|   70                                     GrGLVarying* varying, |   64                                     GrGLSLVarying* varying, | 
|   71                                     GrSLPrecision precision) { |   65                                     GrSLPrecision precision) { | 
|   72     SkASSERT(varying); |   66     SkASSERT(varying); | 
|   73     if (varying->vsVarying()) { |   67     if (varying->vsVarying()) { | 
|   74         fVS.addVarying(name, precision, varying); |   68         fVS.addVarying(name, precision, varying); | 
|   75     } |   69     } | 
|   76     if (this->primitiveProcessor().willUseGeoShader()) { |   70     if (this->primitiveProcessor().willUseGeoShader()) { | 
|   77         fGS.addVarying(name, precision, varying); |   71         fGS.addVarying(name, precision, varying); | 
|   78     } |   72     } | 
|   79     if (varying->fsVarying()) { |   73     if (varying->fsVarying()) { | 
|   80         fFS.addVarying(varying, precision); |   74         fFS.addVarying(varying, precision); | 
|   81     } |   75     } | 
|   82 } |   76 } | 
|   83  |   77  | 
|   84 void GrGLProgramBuilder::addPassThroughAttribute(const GrPrimitiveProcessor::Att
     ribute* input, |   78 void GrGLProgramBuilder::addPassThroughAttribute(const GrPrimitiveProcessor::Att
     ribute* input, | 
|   85                                                  const char* output) { |   79                                                  const char* output) { | 
|   86     GrSLType type = GrVertexAttribTypeToSLType(input->fType); |   80     GrSLType type = GrVertexAttribTypeToSLType(input->fType); | 
|   87     GrGLVertToFrag v(type); |   81     GrGLSLVertToFrag v(type); | 
|   88     this->addVarying(input->fName, &v); |   82     this->addVarying(input->fName, &v); | 
|   89     fVS.codeAppendf("%s = %s;", v.vsOut(), input->fName); |   83     fVS.codeAppendf("%s = %s;", v.vsOut(), input->fName); | 
|   90     fFS.codeAppendf("%s = %s;", output, v.fsIn()); |   84     fFS.codeAppendf("%s = %s;", output, v.fsIn()); | 
|   91 } |   85 } | 
|   92  |   86  | 
|   93 GrGLProgramBuilder::SeparableVaryingHandle GrGLProgramBuilder::addSeparableVaryi
     ng( |   87 GrGLProgramBuilder::SeparableVaryingHandle GrGLProgramBuilder::addSeparableVaryi
     ng( | 
|   94                                                                         const ch
     ar* name, |   88                                                                         const ch
     ar* name, | 
|   95                                                                         GrGLVert
     ToFrag* v, |   89                                                                         GrGLSLVe
     rtToFrag* v, | 
|   96                                                                         GrSLPrec
     ision fsPrecision) { |   90                                                                         GrSLPrec
     ision fsPrecision) { | 
|   97     // This call is not used for non-NVPR backends. |   91     // This call is not used for non-NVPR backends. | 
|   98     SkASSERT(fGpu->glCaps().shaderCaps()->pathRenderingSupport() && |   92     SkASSERT(fGpu->glCaps().shaderCaps()->pathRenderingSupport() && | 
|   99              fArgs.fPrimitiveProcessor->isPathRendering() && |   93              fArgs.fPrimitiveProcessor->isPathRendering() && | 
|  100              !fArgs.fPrimitiveProcessor->willUseGeoShader() && |   94              !fArgs.fPrimitiveProcessor->willUseGeoShader() && | 
|  101              fArgs.fPrimitiveProcessor->numAttribs() == 0); |   95              fArgs.fPrimitiveProcessor->numAttribs() == 0); | 
|  102     this->addVarying(name, v, fsPrecision); |   96     this->addVarying(name, v, fsPrecision); | 
|  103     SeparableVaryingInfo& varyingInfo = fSeparableVaryingInfos.push_back(); |   97     SeparableVaryingInfo& varyingInfo = fSeparableVaryingInfos.push_back(); | 
|  104     varyingInfo.fVariable = this->getFragmentShaderBuilder()->fInputs.back(); |   98     varyingInfo.fVariable = this->getFragmentShaderBuilder()->fInputs.back(); | 
|  105     varyingInfo.fLocation = fSeparableVaryingInfos.count() - 1; |   99     varyingInfo.fLocation = fSeparableVaryingInfos.count() - 1; | 
|  106     return SeparableVaryingHandle(varyingInfo.fLocation); |  100     return SeparableVaryingHandle(varyingInfo.fLocation); | 
|  107 } |  101 } | 
|  108  |  102  | 
|  109 void GrGLProgramBuilder::nameVariable(SkString* out, char prefix, const char* na
     me, bool mangle) { |  | 
|  110     if ('\0' == prefix) { |  | 
|  111         *out = name; |  | 
|  112     } else { |  | 
|  113         out->printf("%c%s", prefix, name); |  | 
|  114     } |  | 
|  115     if (mangle) { |  | 
|  116         if (out->endsWith('_')) { |  | 
|  117             // Names containing "__" are reserved. |  | 
|  118             out->append("x"); |  | 
|  119         } |  | 
|  120         out->appendf("_Stage%d%s", fStageIndex, fFS.getMangleString().c_str()); |  | 
|  121     } |  | 
|  122 } |  | 
|  123  |  | 
|  124 GrGLSLProgramDataManager::UniformHandle GrGLProgramBuilder::internalAddUniformAr
     ray( |  103 GrGLSLProgramDataManager::UniformHandle GrGLProgramBuilder::internalAddUniformAr
     ray( | 
|  125                                                                 uint32_t visibil
     ity, |  104                                                                 uint32_t visibil
     ity, | 
|  126                                                                 GrSLType type, |  105                                                                 GrSLType type, | 
|  127                                                                 GrSLPrecision pr
     ecision, |  106                                                                 GrSLPrecision pr
     ecision, | 
|  128                                                                 const char* name
     , |  107                                                                 const char* name
     , | 
|  129                                                                 bool mangleName, |  108                                                                 bool mangleName, | 
|  130                                                                 int count, |  109                                                                 int count, | 
|  131                                                                 const char** out
     Name) { |  110                                                                 const char** out
     Name) { | 
|  132     SkASSERT(name && strlen(name)); |  111     SkASSERT(name && strlen(name)); | 
|  133     SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFr
     agment_Visibility); |  112     SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFr
     agment_Visibility); | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
|  152     uni.fVariable.setArrayCount(count); |  131     uni.fVariable.setArrayCount(count); | 
|  153     uni.fVisibility = visibility; |  132     uni.fVisibility = visibility; | 
|  154     uni.fVariable.setPrecision(precision); |  133     uni.fVariable.setPrecision(precision); | 
|  155  |  134  | 
|  156     if (outName) { |  135     if (outName) { | 
|  157         *outName = uni.fVariable.c_str(); |  136         *outName = uni.fVariable.c_str(); | 
|  158     } |  137     } | 
|  159     return GrGLSLProgramDataManager::UniformHandle(fUniforms.count() - 1); |  138     return GrGLSLProgramDataManager::UniformHandle(fUniforms.count() - 1); | 
|  160 } |  139 } | 
|  161  |  140  | 
|  162 void GrGLProgramBuilder::appendUniformDecls(ShaderVisibility visibility, |  141 void GrGLProgramBuilder::onAppendUniformDecls(ShaderVisibility visibility, | 
|  163                                             SkString* out) const { |  142                                               SkString* out) const { | 
|  164     for (int i = 0; i < fUniforms.count(); ++i) { |  143     for (int i = 0; i < fUniforms.count(); ++i) { | 
|  165         if (fUniforms[i].fVisibility & visibility) { |  144         if (fUniforms[i].fVisibility & visibility) { | 
|  166             fUniforms[i].fVariable.appendDecl(this->glslCaps(), out); |  145             fUniforms[i].fVariable.appendDecl(this->glslCaps(), out); | 
|  167             out->append(";\n"); |  146             out->append(";\n"); | 
|  168         } |  147         } | 
|  169     } |  148     } | 
|  170 } |  149 } | 
|  171  |  150  | 
|  172 const GrGLContextInfo& GrGLProgramBuilder::ctxInfo() const { |  | 
|  173     return fGpu->ctxInfo(); |  | 
|  174 } |  | 
|  175  |  | 
|  176 const GrGLSLCaps* GrGLProgramBuilder::glslCaps() const { |  151 const GrGLSLCaps* GrGLProgramBuilder::glslCaps() const { | 
|  177     return this->ctxInfo().caps()->glslCaps(); |  152     return this->fGpu->ctxInfo().caps()->glslCaps(); | 
|  178 } |  153 } | 
|  179  |  154  | 
|  180 bool GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr
     4* inputCoverage) { |  155 bool GrGLProgramBuilder::emitAndInstallProcs(GrGLSLExpr4* inputColor, GrGLSLExpr
     4* inputCoverage) { | 
|  181     // First we loop over all of the installed processors and collect coord tran
     sforms.  These will |  156     // First we loop over all of the installed processors and collect coord tran
     sforms.  These will | 
|  182     // be sent to the GrGLPrimitiveProcessor in its emitCode function |  157     // be sent to the GrGLPrimitiveProcessor in its emitCode function | 
|  183     const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); |  158     const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); | 
|  184     int totalTextures = primProc.numTextures(); |  159     int totalTextures = primProc.numTextures(); | 
|  185     const int maxTextureUnits = fGpu->glCaps().maxFragmentTextureUnits(); |  160     const int maxTextureUnits = fGpu->glCaps().maxFragmentTextureUnits(); | 
|  186  |  161  | 
|  187     for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) { |  162     for (int i = 0; i < this->pipeline().numFragmentProcessors(); i++) { | 
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  323     SkASSERT(!fXferProcessor); |  298     SkASSERT(!fXferProcessor); | 
|  324     fXferProcessor = new GrGLInstalledXferProc; |  299     fXferProcessor = new GrGLInstalledXferProc; | 
|  325  |  300  | 
|  326     fXferProcessor->fGLProc.reset(xp.createGLInstance()); |  301     fXferProcessor->fGLProc.reset(xp.createGLInstance()); | 
|  327  |  302  | 
|  328     // Enable dual source secondary output if we have one |  303     // Enable dual source secondary output if we have one | 
|  329     if (xp.hasSecondaryOutput()) { |  304     if (xp.hasSecondaryOutput()) { | 
|  330         fFS.enableSecondaryOutput(); |  305         fFS.enableSecondaryOutput(); | 
|  331     } |  306     } | 
|  332  |  307  | 
|  333     if (this->ctxInfo().caps()->glslCaps()->mustDeclareFragmentShaderOutput()) { |  308     if (this->glslCaps()->mustDeclareFragmentShaderOutput()) { | 
|  334         fFS.enableCustomOutput(); |  309         fFS.enableCustomOutput(); | 
|  335     } |  310     } | 
|  336  |  311  | 
|  337     SkString openBrace; |  312     SkString openBrace; | 
|  338     openBrace.printf("{ // Xfer Processor: %s\n", xp.name()); |  313     openBrace.printf("{ // Xfer Processor: %s\n", xp.name()); | 
|  339     fFS.codeAppend(openBrace.c_str()); |  314     fFS.codeAppend(openBrace.c_str()); | 
|  340  |  315  | 
|  341     SkSTArray<4, GrGLSLTextureSampler> samplers(xp.numTextures()); |  316     SkSTArray<4, GrGLSLTextureSampler> samplers(xp.numTextures()); | 
|  342     this->emitSamplers(xp, &samplers, fXferProcessor); |  317     this->emitSamplers(xp, &samplers, fXferProcessor); | 
|  343  |  318  | 
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  416     SkTDArray<GrGLuint> shadersToDelete; |  391     SkTDArray<GrGLuint> shadersToDelete; | 
|  417     fVS.finalize(kVertex_Visibility); |  392     fVS.finalize(kVertex_Visibility); | 
|  418     if (!this->compileAndAttachShaders(fVS, programID, GR_GL_VERTEX_SHADER, &sha
     dersToDelete)) { |  393     if (!this->compileAndAttachShaders(fVS, programID, GR_GL_VERTEX_SHADER, &sha
     dersToDelete)) { | 
|  419         this->cleanupProgram(programID, shadersToDelete); |  394         this->cleanupProgram(programID, shadersToDelete); | 
|  420         return nullptr; |  395         return nullptr; | 
|  421     } |  396     } | 
|  422  |  397  | 
|  423     // NVPR actually requires a vertex shader to compile |  398     // NVPR actually requires a vertex shader to compile | 
|  424     bool useNvpr = primitiveProcessor().isPathRendering(); |  399     bool useNvpr = primitiveProcessor().isPathRendering(); | 
|  425     if (!useNvpr) { |  400     if (!useNvpr) { | 
|  426         fVS.bindVertexAttributes(programID); |  401         const GrPrimitiveProcessor& primProc = this->primitiveProcessor(); | 
 |  402  | 
 |  403         int vaCount = primProc.numAttribs(); | 
 |  404         for (int i = 0; i < vaCount; i++) { | 
 |  405             GL_CALL(BindAttribLocation(programID, i, primProc.getAttrib(i).fName
     )); | 
 |  406         } | 
|  427     } |  407     } | 
|  428  |  408  | 
|  429     fFS.finalize(kFragment_Visibility); |  409     fFS.finalize(kFragment_Visibility); | 
|  430     if (!this->compileAndAttachShaders(fFS, programID, GR_GL_FRAGMENT_SHADER, &s
     hadersToDelete)) { |  410     if (!this->compileAndAttachShaders(fFS, programID, GR_GL_FRAGMENT_SHADER, &s
     hadersToDelete)) { | 
|  431         this->cleanupProgram(programID, shadersToDelete); |  411         this->cleanupProgram(programID, shadersToDelete); | 
|  432         return nullptr; |  412         return nullptr; | 
|  433     } |  413     } | 
|  434  |  414  | 
|  435     this->bindProgramResourceLocations(programID); |  415     this->bindProgramResourceLocations(programID); | 
|  436  |  416  | 
| (...skipping 16 matching lines...) Expand all  Loading... | 
|  453  |  433  | 
|  454 void GrGLProgramBuilder::bindProgramResourceLocations(GrGLuint programID) { |  434 void GrGLProgramBuilder::bindProgramResourceLocations(GrGLuint programID) { | 
|  455     if (fGpu->glCaps().bindUniformLocationSupport()) { |  435     if (fGpu->glCaps().bindUniformLocationSupport()) { | 
|  456         int count = fUniforms.count(); |  436         int count = fUniforms.count(); | 
|  457         for (int i = 0; i < count; ++i) { |  437         for (int i = 0; i < count; ++i) { | 
|  458             GL_CALL(BindUniformLocation(programID, i, fUniforms[i].fVariable.c_s
     tr())); |  438             GL_CALL(BindUniformLocation(programID, i, fUniforms[i].fVariable.c_s
     tr())); | 
|  459             fUniforms[i].fLocation = i; |  439             fUniforms[i].fLocation = i; | 
|  460         } |  440         } | 
|  461     } |  441     } | 
|  462  |  442  | 
|  463     fFS.bindFragmentShaderLocations(programID); |  443     const GrGLCaps& caps = this->gpu()->glCaps(); | 
 |  444     if (fFS.hasCustomColorOutput() && caps.bindFragDataLocationSupport()) { | 
 |  445         GL_CALL(BindFragDataLocation(programID, 0, | 
 |  446                                      GrGLFragmentShaderBuilder::DeclaredColorOut
     putName())); | 
 |  447     } | 
 |  448     if (fFS.hasSecondaryOutput() && caps.glslCaps()->mustDeclareFragmentShaderOu
     tput()) { | 
 |  449         GL_CALL(BindFragDataLocationIndexed(programID, 0, 1, | 
 |  450                                     GrGLFragmentShaderBuilder::DeclaredSecondary
     ColorOutputName())); | 
 |  451     } | 
|  464  |  452  | 
|  465     // handle NVPR separable varyings |  453     // handle NVPR separable varyings | 
|  466     if (!fGpu->glCaps().shaderCaps()->pathRenderingSupport() || |  454     if (!fGpu->glCaps().shaderCaps()->pathRenderingSupport() || | 
|  467         !fGpu->glPathRendering()->shouldBindFragmentInputs()) { |  455         !fGpu->glPathRendering()->shouldBindFragmentInputs()) { | 
|  468         return; |  456         return; | 
|  469     } |  457     } | 
|  470     int count = fSeparableVaryingInfos.count(); |  458     int count = fSeparableVaryingInfos.count(); | 
|  471     for (int i = 0; i < count; ++i) { |  459     for (int i = 0; i < count; ++i) { | 
|  472         GL_CALL(BindFragmentInputLocation(programID, |  460         GL_CALL(BindFragmentInputLocation(programID, | 
|  473                                           i, |  461                                           i, | 
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  544 } |  532 } | 
|  545  |  533  | 
|  546 ////////////////////////////////////////////////////////////////////////////////
     /////////////////// |  534 ////////////////////////////////////////////////////////////////////////////////
     /////////////////// | 
|  547  |  535  | 
|  548 GrGLInstalledFragProcs::~GrGLInstalledFragProcs() { |  536 GrGLInstalledFragProcs::~GrGLInstalledFragProcs() { | 
|  549     int numProcs = fProcs.count(); |  537     int numProcs = fProcs.count(); | 
|  550     for (int i = 0; i < numProcs; ++i) { |  538     for (int i = 0; i < numProcs; ++i) { | 
|  551         delete fProcs[i]; |  539         delete fProcs[i]; | 
|  552     } |  540     } | 
|  553 } |  541 } | 
| OLD | NEW |