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 
ShaderParser()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 
ShaderParser(GLenum type)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 
setSrc(const Version & ver,GLsizei count,const GLchar ** strings,const GLint * length)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 }
parsedLines()72 const GLchar** ShaderParser::parsedLines() {
73       m_parsedLines = (GLchar*)m_parsedSrc.c_str();
74       return const_cast<const GLchar**> (&m_parsedLines);
75 };
76 
getOriginalSrc()77 const char* ShaderParser::getOriginalSrc(){
78     return m_originalSrc;
79 }
80 
parseLineNumbers()81 void ShaderParser::parseLineNumbers()
82 {
83     m_parsedSrc += "#line 1\n";
84 }
85 
parseOriginalSrc()86 void ShaderParser::parseOriginalSrc() {
87     m_parsedSrc+=m_src;
88 }
89 
parseGLSLversion()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 
parseBuiltinConstants()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 
parseOmitPrecision()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 
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