• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ShaderParser()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 
ShaderParser(GLenum type)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 
setSrc(const Version & ver,GLsizei count,const GLchar ** strings,const GLint * length)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 }
parsedLines()71 const GLchar** ShaderParser::parsedLines() {
72       m_parsedLines = (GLchar*)m_parsedSrc.c_str();
73       return const_cast<const GLchar**> (&m_parsedLines);
74 };
75 
getOriginalSrc()76 const char* ShaderParser::getOriginalSrc(){
77     return m_originalSrc;
78 }
79 
parseLineNumbers()80 void ShaderParser::parseLineNumbers()
81 {
82     m_parsedSrc += "#line 1\n";
83 }
84 
parseOriginalSrc()85 void ShaderParser::parseOriginalSrc() {
86     m_parsedSrc+=m_src;
87 }
88 
parseGLSLversion()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 
parseBuiltinConstants()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 
parseOmitPrecision()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 
parseExtendDefaultPrecision()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 
clearParsedSrc()319 void ShaderParser::clearParsedSrc(){
320     m_parsedSrc.clear();
321 }
322 
getType()323 GLenum ShaderParser::getType() {
324     return m_type;
325 }
326 
setInfoLog(GLchar * infoLog)327 void ShaderParser::setInfoLog(GLchar* infoLog)
328 {
329     delete[] m_infoLog;
330     m_infoLog = infoLog;
331 }
332 
getInfoLog()333 GLchar* ShaderParser::getInfoLog()
334 {
335     return m_infoLog;
336 }
337 
~ShaderParser()338 ShaderParser::~ShaderParser(){
339     clearParsedSrc();
340     if (m_originalSrc)
341         free(m_originalSrc);
342     delete[] m_infoLog;
343 }
344