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 <stdlib.h>
     19 #include <string.h>
     20 
     21 ShaderParser::ShaderParser():ObjectData(SHADER_DATA),
     22                              m_type(0),
     23                              m_originalSrc(NULL),
     24                              m_parsedLines(NULL) {
     25     m_infoLog = new GLchar[1];
     26     m_infoLog[0] = '\0';
     27 };
     28 
     29 ShaderParser::ShaderParser(GLenum type):ObjectData(SHADER_DATA),
     30                                         m_type(type),
     31                                         m_originalSrc(NULL),
     32                                         m_parsedLines(NULL) {
     33 
     34     m_infoLog = new GLchar[1];
     35     m_infoLog[0] = '\0';
     36 };
     37 
     38 void ShaderParser::setSrc(const Version& ver,GLsizei count,const GLchar** strings,const GLint* length){
     39     for(int i = 0;i<count;i++){
     40         m_src.append(strings[i]);
     41     }
     42     //store original source
     43     if (m_originalSrc)
     44         free(m_originalSrc);
     45     m_originalSrc = strdup(m_src.c_str());
     46 
     47     clearParsedSrc();
     48 
     49     // parseGLSLversion must be called first since #version should be the
     50     // first token in the shader source.
     51     parseGLSLversion();
     52     parseBuiltinConstants();
     53     /*
     54       version 1.30.10 is the first version of GLSL Language containing precision qualifiers
     55       if the glsl version is less than 1.30.10 than we will use a shader parser which omits
     56       all precision qualifiers from the shader source , otherwise we will use a shader parser
     57       which set the default precisions to be the same as the default precisions of GLSL ES
     58     */
     59 #if 0
     60     if(ver < Version(1,30,10)){
     61         parseOmitPrecision();
     62      } else {
     63         parseExtendDefaultPrecision();
     64      }
     65 #else
     66     //XXX: Until proved otherwise, glsl doesn't know/use those precision macros, so we omit then
     67     parseOmitPrecision();
     68 #endif
     69     parseLineNumbers();
     70     parseOriginalSrc();
     71 }
     72 const GLchar** ShaderParser::parsedLines() {
     73       m_parsedLines = (GLchar*)m_parsedSrc.c_str();
     74       return const_cast<const GLchar**> (&m_parsedLines);
     75 };
     76 
     77 const char* ShaderParser::getOriginalSrc(){
     78     return m_originalSrc;
     79 }
     80 
     81 void ShaderParser::parseLineNumbers()
     82 {
     83     m_parsedSrc += "#line 1\n";
     84 }
     85 
     86 void ShaderParser::parseOriginalSrc() {
     87     m_parsedSrc+=m_src;
     88 }
     89 
     90 void ShaderParser::parseGLSLversion() {
     91 
     92     //
     93     // find in shader the #version token if exist.
     94     // That token should be the first non-comment or blank token
     95     //
     96     const char *src = m_src.c_str();
     97     const int minGLSLVersion = 120;
     98     int glslVersion = minGLSLVersion;
     99     enum {
    100         PARSE_NONE,
    101         PARSE_IN_C_COMMENT,
    102         PARSE_IN_LINE_COMMENT
    103     } parseState = PARSE_NONE;
    104     const char *c = src;
    105 
    106     while( c && *c != '\0') {
    107         if (parseState == PARSE_IN_C_COMMENT) {
    108             if (*c == '*' && *(c+1) == '/') {
    109                 parseState = PARSE_NONE;
    110                 c += 2;
    111             }
    112             else c++;
    113         }
    114         else if (parseState == PARSE_IN_LINE_COMMENT) {
    115             if (*c == '\n') {
    116                 parseState = PARSE_NONE;
    117             }
    118             c++;
    119         }
    120         else if (*c == '/' && *(c+1) == '/') {
    121             parseState = PARSE_IN_LINE_COMMENT;
    122             c += 2;
    123         }
    124         else if (*c == '/' && *(c+1) == '*') {
    125             parseState = PARSE_IN_C_COMMENT;
    126             c += 2;
    127         }
    128         else if (*c == ' ' || *c == '\t' || *c == '\r' || *c == '\n') {
    129             c++;
    130         }
    131         else {
    132             //
    133             // We have reached the first non-blank character outside
    134             // a comment, this must be a #version token or else #version
    135             // token does not exist in this shader source.
    136             //
    137             if (!strncmp(c,"#version",8)) {
    138                 int ver;
    139                 if (sscanf(c+8,"%d",&ver) == 1) {
    140                     //
    141                     // parsed version string correctly, blank out the
    142                     // version token from the source, we will add it later at
    143                     // the begining of the shader.
    144                     //
    145                     char *cc = (char *)c;
    146                     for (int i=0; i<8; i++,cc++) *cc = ' ';
    147                     while (*cc < '0' || *cc > '9') { *cc = ' '; cc++; }
    148                     while (*cc >= '0' && *cc <= '9') { *cc = ' '; cc++; }
    149 
    150                     // Use the version from the source but only if
    151                     // it is larger than our minGLSLVersion
    152                     if (ver > minGLSLVersion) glslVersion = ver;
    153                 }
    154             }
    155 
    156             //
    157             // break the loop, no need to go further on the source.
    158             break;
    159         }
    160     }
    161 
    162     //
    163     // allow to force GLSL version through environment variable
    164     //
    165     const char *forceVersion = getenv("GOOGLE_GLES_FORCE_GLSL_VERSION");
    166     if (forceVersion) {
    167         int ver;
    168         if (sscanf(forceVersion,"%d",&ver) == 1) {
    169             glslVersion = ver;
    170         }
    171     }
    172 
    173     //
    174     // if glslVersion is defined, add it to the parsed source
    175     //
    176     if (glslVersion > 0) {
    177         char vstr[16];
    178         sprintf(vstr,"%d",glslVersion);
    179         m_parsedSrc += std::string("#version ") +
    180                        std::string(vstr) +
    181                        std::string("\n");
    182     }
    183 }
    184 
    185 void ShaderParser::parseBuiltinConstants()
    186 {
    187     m_parsedSrc +=
    188                    "const int _translator_gl_MaxVertexUniformVectors = 256;\n"
    189                    "const int _translator_gl_MaxFragmentUniformVectors = 256;\n"
    190                    "const int _translator_gl_MaxVaryingVectors = 15;\n"
    191                    "#define gl_MaxVertexUniformVectors _translator_gl_MaxVertexUniformVectors\n"
    192                    "#define gl_MaxFragmentUniformVectors _translator_gl_MaxFragmentUniformVectors\n"
    193                    "#define gl_MaxVaryingVectors _translator_gl_MaxVaryingVectors\n";
    194 
    195 }
    196 
    197 void ShaderParser::parseOmitPrecision(){
    198 
    199     //defines we need to add in order to Omit precisions qualifiers
    200     static const GLchar defines[] = {
    201                                          "#define GLES 1\n"
    202                                          "#define lowp \n"
    203                                          "#define mediump \n"
    204                                          "#define highp \n"
    205                                      };
    206     m_parsedSrc+=defines;
    207 
    208     //
    209     // parse the source and blank out precision statements
    210     // which has the following syntax:
    211     //   precision {qualifier} {type};
    212     // where {qualifier} is one of lowp,mediump or hightp
    213     // type is any valid GLES defined type (we do not check that here!)
    214     // NOTE: This is needed in order to workaround driver bug in
    215     //       Intel/Linux where the compiler does not get statement like
    216     //       "float;", otherwise we could just define a macro named
    217     //       precision to be empty.
    218     //
    219     const char *src = m_src.c_str();
    220 
    221     enum {
    222         PRECISION,
    223         QUALIFIER,
    224         SEMICOLON
    225     } statementState = PRECISION;
    226     const char *precision = 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