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