Home | History | Annotate | Download | only in GLES_V2
      1 /*
      2 * Copyright 2011 The Android Open Source Project
      3 *
      4 * Licensed under the Apache License, Version 2.0 (the "License");
      5 * you may not use this file except in compliance with the License.
      6 * You may obtain a copy of the License at
      7 *
      8 * http://www.apache.org/licenses/LICENSE-2.0
      9 *
     10 * Unless required by applicable law or agreed to in writing, software
     11 * distributed under the License is distributed on an "AS IS" BASIS,
     12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 * See the License for the specific language governing permissions and
     14 * limitations under the License.
     15 */
     16 
     17 #include "ShaderParser.h"
     18 #include <string.h>
     19 
     20 ShaderParser::ShaderParser():ObjectData(SHADER_DATA),
     21                              m_type(0),
     22                              m_originalSrc(NULL),
     23                              m_parsedLines(NULL) {
     24     m_infoLog = new GLchar[1];
     25     m_infoLog[0] = '\0';
     26 };
     27 
     28 ShaderParser::ShaderParser(GLenum type):ObjectData(SHADER_DATA),
     29                                         m_type(type),
     30                                         m_originalSrc(NULL),
     31                                         m_parsedLines(NULL) {
     32 
     33     m_infoLog = new GLchar[1];
     34     m_infoLog[0] = '\0';
     35 };
     36 
     37 void ShaderParser::setSrc(const Version& ver,GLsizei count,const GLchar** strings,const GLint* length){
     38     for(int i = 0;i<count;i++){
     39         m_src.append(strings[i]);
     40     }
     41     //store original source
     42     if (m_originalSrc)
     43         free(m_originalSrc);
     44     m_originalSrc = strdup(m_src.c_str());
     45 
     46     clearParsedSrc();
     47 
     48     // parseGLSLversion must be called first since #version should be the
     49     // first token in the shader source.
     50     parseGLSLversion();
     51     parseBuiltinConstants();
     52     /*
     53       version 1.30.10 is the first version of GLSL Language containing precision qualifiers
     54       if the glsl version is less than 1.30.10 than we will use a shader parser which omits
     55       all precision qualifiers from the shader source , otherwise we will use a shader parser
     56       which set the default precisions to be the same as the default precisions of GLSL ES
     57     */
     58 #if 0
     59     if(ver < Version(1,30,10)){
     60         parseOmitPrecision();
     61      } else {
     62         parseExtendDefaultPrecision();
     63      }
     64 #else
     65     //XXX: Until proved otherwise, glsl doesn't know/use those precision macros, so we omit then
     66     parseOmitPrecision();
     67 #endif
     68     parseLineNumbers();
     69     parseOriginalSrc();
     70 }
     71 const GLchar** ShaderParser::parsedLines() {
     72       m_parsedLines = (GLchar*)m_parsedSrc.c_str();
     73       return const_cast<const GLchar**> (&m_parsedLines);
     74 };
     75 
     76 const char* ShaderParser::getOriginalSrc(){
     77     return m_originalSrc;
     78 }
     79 
     80 void ShaderParser::parseLineNumbers()
     81 {
     82     m_parsedSrc += "#line 1\n";
     83 }
     84 
     85 void ShaderParser::parseOriginalSrc() {
     86     m_parsedSrc+=m_src;
     87 }
     88 
     89 void ShaderParser::parseGLSLversion() {
     90 
     91     //
     92     // find in shader the #version token if exist.
     93     // That token should be the first non-comment or blank token
     94     //
     95     const char *src = m_src.c_str();
     96     const int minGLSLVersion = 120;
     97     int glslVersion = minGLSLVersion;
     98     enum {
     99         PARSE_NONE,
    100         PARSE_IN_C_COMMENT,
    101         PARSE_IN_LINE_COMMENT
    102     } parseState = PARSE_NONE;
    103     const char *c = src;
    104 
    105     while( c && *c != '\0') {
    106         if (parseState == PARSE_IN_C_COMMENT) {
    107             if (*c == '*' && *(c+1) == '/') {
    108                 parseState = PARSE_NONE;
    109                 c += 2;
    110             }
    111             else c++;
    112         }
    113         else if (parseState == PARSE_IN_LINE_COMMENT) {
    114             if (*c == '\n') {
    115                 parseState = PARSE_NONE;
    116             }
    117             c++;
    118         }
    119         else if (*c == '/' && *(c+1) == '/') {
    120             parseState = PARSE_IN_LINE_COMMENT;
    121             c += 2;
    122         }
    123         else if (*c == '/' && *(c+1) == '*') {
    124             parseState = PARSE_IN_C_COMMENT;
    125             c += 2;
    126         }
    127         else if (*c == ' ' || *c == '\t' || *c == '\r' || *c == '\n') {
    128             c++;
    129         }
    130         else {
    131             //
    132             // We have reached the first non-blank character outside
    133             // a comment, this must be a #version token or else #version
    134             // token does not exist in this shader source.
    135             //
    136             if (!strncmp(c,"#version",8)) {
    137                 int ver;
    138                 if (sscanf(c+8,"%d",&ver) == 1) {
    139                     //
    140                     // parsed version string correctly, blank out the
    141                     // version token from the source, we will add it later at
    142                     // the begining of the shader.
    143                     //
    144                     char *cc = (char *)c;
    145                     for (int i=0; i<8; i++,cc++) *cc = ' ';
    146                     while (*cc < '0' || *cc > '9') { *cc = ' '; cc++; }
    147                     while (*cc >= '0' && *cc <= '9') { *cc = ' '; cc++; }
    148 
    149                     // Use the version from the source but only if
    150                     // it is larger than our minGLSLVersion
    151                     if (ver > minGLSLVersion) glslVersion = ver;
    152                 }
    153             }
    154 
    155             //
    156             // break the loop, no need to go further on the source.
    157             break;
    158         }
    159     }
    160 
    161     //
    162     // allow to force GLSL version through environment variable
    163     //
    164     const char *forceVersion = getenv("GOOGLE_GLES_FORCE_GLSL_VERSION");
    165     if (forceVersion) {
    166         int ver;
    167         if (sscanf(forceVersion,"%d",&ver) == 1) {
    168             glslVersion = ver;
    169         }
    170     }
    171 
    172     //
    173     // if glslVersion is defined, add it to the parsed source
    174     //
    175     if (glslVersion > 0) {
    176         char vstr[16];
    177         sprintf(vstr,"%d",glslVersion);
    178         m_parsedSrc += std::string("#version ") +
    179                        std::string(vstr) +
    180                        std::string("\n");
    181     }
    182 }
    183 
    184 void ShaderParser::parseBuiltinConstants()
    185 {
    186     m_parsedSrc +=
    187                    "const int _translator_gl_MaxVertexUniformVectors = 256;\n"
    188                    "const int _translator_gl_MaxFragmentUniformVectors = 256;\n"
    189                    "const int _translator_gl_MaxVaryingVectors = 15;\n"
    190                    "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n"
    191                    "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n"
    192                    "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n";
    193 
    194 }
    195 
    196 void ShaderParser::parseOmitPrecision(){
    197 
    198     //defines we need to add in order to Omit precisions qualifiers
    199     static const GLchar defines[] = {
    200                                          "#define GLES 1\n"
    201                                          "#define lowp \n"
    202                                          "#define mediump \n"
    203                                          "#define highp \n"
    204                                      };
    205     m_parsedSrc+=defines;
    206 
    207     //
    208     // parse the source and blank out precision statements
    209     // which has the following syntax:
    210     //   precision {qualifier} {type};
    211     // where {qualifier} is one of lowp,mediump or hightp
    212     // type is any valid GLES defined type (we do not check that here!)
    213     // NOTE: This is needed in order to workaround driver bug in
    214     //       Intel/Linux where the compiler does not get statement like
    215     //       "float;", otherwise we could just define a macro named
    216     //       precision to be empty.
    217     //
    218     const char *src = m_src.c_str();
    219 
    220     enum {
    221         PRECISION,
    222         QUALIFIER,
    223         SEMICOLON
    224     } statementState = PRECISION;
    225     const char *precision = NULL;
    226     const char *delimiter = NULL;
    227 
    228     enum {
    229         PARSE_NONE,
    230         PARSE_IN_C_COMMENT,
    231         PARSE_IN_LINE_COMMENT
    232     } parseState = PARSE_NONE;
    233     const char *c = src;
    234     const char *t = NULL;
    235 
    236     #define IS_DELIMITER(c) ( (c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n' )
    237     #define IS_TOKEN_START(c) ( ((c) >= 'a' && (c) <='z') || ((c) >= 'A' && (c) <= 'Z') )
    238     #define IS_TOKEN_DELIMITER(c) ( IS_DELIMITER(c) || (c) == ';' )
    239 
    240     while( c && *c != '\0') {
    241         if (parseState == PARSE_IN_C_COMMENT) {
    242             if (*c == '*' && *(c+1) == '/') {
    243                 parseState = PARSE_NONE;
    244                 c += 2;
    245             }
    246             else c++;
    247         }
    248         else if (parseState == PARSE_IN_LINE_COMMENT) {
    249             if (*c == '\n') {
    250                 parseState = PARSE_NONE;
    251             }
    252             c++;
    253         }
    254         else if (*c == '/' && *(c+1) == '/') {
    255             parseState = PARSE_IN_LINE_COMMENT;
    256             c += 2;
    257         }
    258         else if (*c == '/' && *(c+1) == '*') {
    259             parseState = PARSE_IN_C_COMMENT;
    260             c += 2;
    261         }
    262         else if (t && IS_TOKEN_DELIMITER(*c)) {
    263             int tokenLen = c - t;
    264             switch (statementState) {
    265             case PRECISION:
    266                 if (tokenLen == 9 && !strncmp(t,"precision",9)) {
    267                     statementState = QUALIFIER;
    268                     precision = t;
    269                 }
    270                 break;
    271             case QUALIFIER:
    272                 if ((tokenLen == 4 && !strncmp(t,"lowp",4)) ||
    273                     (tokenLen == 7 && !strncmp(t,"mediump",7)) ||
    274                     (tokenLen == 5 && !strncmp(t,"highp",5))) {
    275                     statementState = SEMICOLON;
    276                 }
    277                 else {
    278                     statementState = PRECISION;
    279                 }
    280                 break;
    281             case SEMICOLON:
    282                 if (*c == ';') {
    283                     for (char *r = (char *)precision; r<=c ; ++r) {
    284                         *r = ' '; //blank the character
    285                     }
    286                 }
    287                 statementState = PRECISION; //search for the next precision line
    288                 break;
    289             default:
    290                 break;
    291             }
    292             c++;
    293             t = NULL;
    294         }
    295         else if (IS_DELIMITER(*c)) {
    296             c++;
    297         }
    298         else {
    299             if (!t && IS_TOKEN_START(*c)) {
    300                 t = c;
    301             }
    302             c++;
    303         }
    304     }
    305 }
    306 
    307 void ShaderParser::parseExtendDefaultPrecision(){
    308 
    309     //the precision lines which we need to add to the shader
    310     static const GLchar extend[] = {
    311                                       "#define GLES 1\n"
    312                                       "precision lowp sampler2D;\n"
    313                                       "precision lowp samplerCube;\n"
    314                                    };
    315 
    316     m_parsedSrc+=extend;
    317 }
    318 
    319 void ShaderParser::clearParsedSrc(){
    320     m_parsedSrc.clear();
    321 }
    322 
    323 GLenum ShaderParser::getType() {
    324     return m_type;
    325 }
    326 
    327 void ShaderParser::setInfoLog(GLchar* infoLog)
    328 {
    329     delete[] m_infoLog;
    330     m_infoLog = infoLog;
    331 }
    332 
    333 GLchar* ShaderParser::getInfoLog()
    334 {
    335     return m_infoLog;
    336 }
    337 
    338 ShaderParser::~ShaderParser(){
    339     clearParsedSrc();
    340     if (m_originalSrc)
    341         free(m_originalSrc);
    342     delete[] m_infoLog;
    343 }
    344