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