|
1 /* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
2 * |
|
3 * Permission is hereby granted, free of charge, to any person obtaining a |
|
4 * copy of this software and associated documentation files (the "Software"), |
|
5 * to deal in the Software without restriction, including without limitation |
|
6 * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
7 * and/or sell copies of the Software, and to permit persons to whom the |
|
8 * Software is furnished to do so, subject to the following conditions: |
|
9 * |
|
10 * The above copyright notice and this permission notice shall be included |
|
11 * in all copies or substantial portions of the Software. |
|
12 * |
|
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
|
14 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
16 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
|
17 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
|
18 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
19 * |
|
20 * Initial Contributors: |
|
21 * Nokia Corporation - initial contribution. |
|
22 * |
|
23 * Contributors: |
|
24 * |
|
25 * Description: |
|
26 * |
|
27 */ |
|
28 |
|
29 #include "common.h" |
|
30 #include "hgl.h" |
|
31 #include "context.h" |
|
32 #include <string.h> |
|
33 |
|
34 DGLShader* DGLShader_create(GLuint name) |
|
35 { |
|
36 DGLShader* shader = malloc(sizeof(DGLShader)); |
|
37 if(shader == NULL) |
|
38 { |
|
39 return NULL; |
|
40 } |
|
41 |
|
42 shader->obj.name = name; |
|
43 shader->obj.next = NULL; |
|
44 |
|
45 shader->source = NULL; |
|
46 shader->length = 0; |
|
47 |
|
48 return shader; |
|
49 } |
|
50 |
|
51 void DGLShader_destroy(DGLShader *shader) |
|
52 { |
|
53 DGLES2_ASSERT(shader != NULL); |
|
54 if(shader->source != NULL) |
|
55 { |
|
56 free(shader->source); |
|
57 shader->source = NULL; |
|
58 } |
|
59 free(shader); |
|
60 } |
|
61 |
|
62 GL_APICALL_BUILD void GL_APIENTRY glCompileShader(GLuint shader) |
|
63 { |
|
64 DGLES2_ENTER(); |
|
65 ctx->hgl.CompileShader(shader); |
|
66 DGLES2_LEAVE(); |
|
67 } |
|
68 |
|
69 GL_APICALL_BUILD GLuint GL_APIENTRY glCreateShader(GLenum type) |
|
70 { |
|
71 DGLES2_ENTER_RET(0); |
|
72 DGLES2_ERROR_IF_RET(type != GL_VERTEX_SHADER && type != GL_FRAGMENT_SHADER, GL_INVALID_ENUM, 0); |
|
73 { |
|
74 GLuint name = ctx->hgl.CreateShader(type); |
|
75 if(DGLContext_getHostError(ctx) == GL_NO_ERROR && name != 0) |
|
76 { |
|
77 DGLContext_createShader(ctx, name); |
|
78 } |
|
79 |
|
80 DGLES2_LEAVE_RET(name); |
|
81 } |
|
82 } |
|
83 |
|
84 GL_APICALL_BUILD void GL_APIENTRY glDeleteShader(GLuint shader) |
|
85 { |
|
86 DGLES2_ENTER(); |
|
87 if(shader != 0) |
|
88 { |
|
89 DGLES2_ERROR_IF(ctx->hgl.IsProgram(shader), GL_INVALID_OPERATION); |
|
90 DGLES2_ERROR_IF(!ctx->hgl.IsShader(shader), GL_INVALID_VALUE); |
|
91 ctx->hgl.DeleteShader(shader); |
|
92 if(DGLContext_getHostError(ctx) == GL_NO_ERROR) |
|
93 { |
|
94 DGLContext_destroyShader(ctx, shader); |
|
95 } |
|
96 } |
|
97 DGLES2_LEAVE(); |
|
98 } |
|
99 |
|
100 GL_APICALL_BUILD void GL_APIENTRY glGetShaderiv(GLuint shader, GLenum pname, GLint* params) |
|
101 { |
|
102 DGLES2_ENTER(); |
|
103 DGLES2_ERROR_IF(ctx->hgl.IsProgram(shader), GL_INVALID_OPERATION); |
|
104 DGLES2_ERROR_IF(!ctx->hgl.IsShader(shader), GL_INVALID_VALUE); |
|
105 if(pname == GL_SHADER_SOURCE_LENGTH) |
|
106 { |
|
107 DGLShader* shader_obj = DGLContext_findShader(ctx, shader); |
|
108 DGLES2_ASSERT(shader_obj != NULL); |
|
109 *params = shader_obj->length + 1; |
|
110 } |
|
111 else |
|
112 { |
|
113 ctx->hgl.GetShaderiv(shader, pname, params); |
|
114 } |
|
115 DGLES2_LEAVE(); |
|
116 } |
|
117 |
|
118 GL_APICALL_BUILD void GL_APIENTRY glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) |
|
119 { |
|
120 DGLES2_ENTER(); |
|
121 DGLES2_ERROR_IF(ctx->hgl.IsProgram(shader), GL_INVALID_OPERATION); |
|
122 DGLES2_ERROR_IF(!ctx->hgl.IsShader(shader), GL_INVALID_VALUE); |
|
123 ctx->hgl.GetShaderInfoLog(shader, bufsize, length, infolog); |
|
124 DGLES2_LEAVE(); |
|
125 } |
|
126 |
|
127 GL_APICALL_BUILD void GL_APIENTRY glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) |
|
128 { |
|
129 DGLES2_ENTER(); |
|
130 DGLES2_ERROR_IF(shadertype != GL_VERTEX_SHADER && shadertype != GL_FRAGMENT_SHADER, GL_INVALID_ENUM); |
|
131 // Values from the GL ES and GLSL specifications. |
|
132 switch(precisiontype) |
|
133 { |
|
134 case GL_LOW_FLOAT: |
|
135 case GL_MEDIUM_FLOAT: |
|
136 case GL_HIGH_FLOAT: |
|
137 range[0] = 127; |
|
138 range[1] = 127; |
|
139 *precision = 23; |
|
140 break; |
|
141 |
|
142 case GL_LOW_INT: |
|
143 case GL_MEDIUM_INT: |
|
144 case GL_HIGH_INT: |
|
145 range[0] = 15; |
|
146 range[1] = 14; |
|
147 *precision = 0; |
|
148 break; |
|
149 } |
|
150 DGLES2_LEAVE(); |
|
151 } |
|
152 |
|
153 GL_APICALL_BUILD void GL_APIENTRY glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) |
|
154 { |
|
155 DGLES2_ENTER(); |
|
156 DGLES2_ERROR_IF(ctx->hgl.IsProgram(shader), GL_INVALID_OPERATION); |
|
157 DGLES2_ERROR_IF(!ctx->hgl.IsShader(shader), GL_INVALID_VALUE); |
|
158 DGLES2_ERROR_IF(bufsize < 0, GL_INVALID_VALUE); |
|
159 { |
|
160 DGLShader* shader_obj = DGLContext_findShader(ctx, shader); |
|
161 DGLES2_ASSERT(shader_obj != NULL); |
|
162 |
|
163 if(length != NULL) |
|
164 { |
|
165 *length = 0; |
|
166 } |
|
167 |
|
168 if(source != NULL) |
|
169 { |
|
170 GLsizei num_chars = shader_obj->length < bufsize - 1 ? shader_obj->length : bufsize - 1; |
|
171 if(num_chars > 0) |
|
172 { |
|
173 strncpy(source, shader_obj->source, num_chars); |
|
174 source[num_chars] = 0; |
|
175 if(length != NULL) |
|
176 { |
|
177 *length = num_chars; |
|
178 } |
|
179 } |
|
180 } |
|
181 } |
|
182 DGLES2_LEAVE(); |
|
183 } |
|
184 |
|
185 GL_APICALL_BUILD GLboolean GL_APIENTRY glIsShader(GLuint shader) |
|
186 { |
|
187 DGLES2_ENTER_RET(GL_FALSE); |
|
188 DGLES2_LEAVE_RET(ctx->hgl.IsShader(shader)); |
|
189 } |
|
190 |
|
191 GL_APICALL_BUILD void GL_APIENTRY glReleaseShaderCompiler(void) |
|
192 { |
|
193 DGLES2_ENTER(); |
|
194 // No-op. |
|
195 DGLES2_LEAVE(); |
|
196 } |
|
197 |
|
198 GL_APICALL_BUILD void GL_APIENTRY glShaderBinary(GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length) |
|
199 { |
|
200 DGLES2_ENTER(); |
|
201 // No supported formats. |
|
202 DGLES2_ERROR(GL_INVALID_ENUM); |
|
203 DGLES2_LEAVE(); |
|
204 } |
|
205 |
|
206 static const char *opengl_strtok(const char *s, int *n) |
|
207 { |
|
208 static char *buffer = 0; |
|
209 static int buffersize = -1; |
|
210 static const char *delim = " \t\n\r()[]{},;:"; |
|
211 static const char *prev = 0; |
|
212 int cComment = 0; |
|
213 int cppComment = 0; |
|
214 |
|
215 if (!s) { |
|
216 if (!*prev || !*n) { |
|
217 if (buffer) { |
|
218 free(buffer); |
|
219 buffer = 0; |
|
220 buffersize = -1; |
|
221 } |
|
222 prev = 0; |
|
223 return 0; |
|
224 } |
|
225 s = prev; |
|
226 } else { |
|
227 if (buffer) { |
|
228 free(buffer); |
|
229 buffer = 0; |
|
230 buffersize = -1; |
|
231 } |
|
232 prev = s; |
|
233 } |
|
234 |
|
235 if( *n && *s == '/') { |
|
236 if(*(s+1) == '*') cComment = 1; |
|
237 if(*(s+1) == '/') cppComment = 1; |
|
238 } |
|
239 if( cComment == 1 || cppComment == 1) { |
|
240 for(; *n && (cComment == 1 || cppComment == 1); s++, (*n)--) { |
|
241 if(cComment == 1 && *s == '*' && *(s+1) == '/' ) { |
|
242 cComment = 0; |
|
243 } |
|
244 if(cppComment == 1 && *s == '\n') { |
|
245 cppComment = 0; |
|
246 } |
|
247 } |
|
248 } else { |
|
249 for (; *n && strchr(delim, *s); s++, (*n)--); |
|
250 } |
|
251 |
|
252 if(s - prev > 0) { |
|
253 if (buffersize < s - prev) { |
|
254 buffersize = s - prev; |
|
255 if (buffer) { |
|
256 free(buffer); |
|
257 } |
|
258 buffer = malloc(buffersize + 1); |
|
259 } |
|
260 memcpy(buffer, prev, s - prev); |
|
261 buffer[s - prev] = 0; |
|
262 prev = s; |
|
263 } else { |
|
264 const char *e = s; |
|
265 for (; *n && *e && !strchr(delim, *e); e++, (*n)--); |
|
266 prev = e; |
|
267 if (buffersize < e - s) { |
|
268 buffersize = e - s; |
|
269 if (buffer) { |
|
270 free(buffer); |
|
271 } |
|
272 buffer = malloc(buffersize + 1); |
|
273 } |
|
274 memcpy(buffer, s, e - s); |
|
275 buffer[e - s] = 0; |
|
276 } |
|
277 return buffer; |
|
278 } |
|
279 |
|
280 static char* do_eglShaderPatch(char *source, int len, int *patched_len) |
|
281 { |
|
282 /* DISCLAIMER: this is not a full-blown shader parser but a simple |
|
283 * implementation which tries to remove the OpenGL ES shader |
|
284 * "precision" statements and precision qualifiers "lowp", "mediump" |
|
285 * and "highp" from the specified shader source, replace built-in |
|
286 * constants that were renamed in GLSL ES ("gl_MaxVertexUniformVectors", |
|
287 * "gl_MaxFragmentUniformVectors" and "gl_MaxVaryingVectors") |
|
288 * and insert a "#version 120" directive in the beginning of the source |
|
289 * or replace an existing "#version 100" directive. */ |
|
290 DGLES2_ASSERT(source != NULL); |
|
291 DGLES2_ASSERT(len >= 0); |
|
292 { |
|
293 #ifndef DGLES2_ALLOW_GLSL_110 |
|
294 GLboolean version_found = GL_FALSE; |
|
295 #endif |
|
296 int buffer_size; |
|
297 char *patched; |
|
298 const char *p; |
|
299 |
|
300 *patched_len = 0; |
|
301 buffer_size = len; |
|
302 patched = malloc(buffer_size + 1); |
|
303 if(patched == NULL) { |
|
304 return NULL; |
|
305 } |
|
306 |
|
307 p = opengl_strtok(source, &len); |
|
308 for (; p; p = opengl_strtok(0, &len)) { |
|
309 if (!strcmp(p, "lowp") || !strcmp(p, "mediump") || !strcmp(p, "highp")) { |
|
310 continue; |
|
311 } else if (!strcmp(p, "precision")) { |
|
312 do { |
|
313 p = opengl_strtok(0, &len); |
|
314 } while(p && !strchr(p, ';')); |
|
315 } else { |
|
316 int tok_len; |
|
317 if (!strcmp(p, "gl_MaxVertexUniformVectors")) { |
|
318 p = "(gl_MaxVertexUniformComponents / 4)"; |
|
319 } else if (!strcmp(p, "gl_MaxFragmentUniformVectors")) { |
|
320 p = "(gl_MaxFragmentUniformComponents / 4)"; |
|
321 } else if (!strcmp(p, "gl_MaxVaryingVectors")) { |
|
322 p = "(gl_MaxVaryingFloats / 4)"; |
|
323 } |
|
324 #ifndef DGLES2_ALLOW_GLSL_110 |
|
325 else if (!strcmp(p, "#version")) { |
|
326 p = opengl_strtok(0, &len); |
|
327 if (!strcmp(p, "100")) { |
|
328 p = "#version 120"; |
|
329 version_found = GL_TRUE; |
|
330 } |
|
331 } else if (!strcmp(p, "#")) { |
|
332 p = opengl_strtok(0, &len); |
|
333 if (!strcmp(p, "version")) { |
|
334 p = opengl_strtok(0, &len); |
|
335 if (!strcmp(p, "100")) { |
|
336 p = "#version 120"; |
|
337 version_found = GL_TRUE; |
|
338 } |
|
339 } |
|
340 } |
|
341 #endif // !DGLES2_ALLOW_GLSL_110 |
|
342 tok_len = strlen(p); |
|
343 if(*patched_len + tok_len > buffer_size) { |
|
344 buffer_size *= 2; |
|
345 patched = realloc(patched, buffer_size + 1); |
|
346 if(patched == NULL) { |
|
347 return NULL; |
|
348 } |
|
349 } |
|
350 memcpy(patched + *patched_len, p, tok_len); |
|
351 *patched_len += tok_len; |
|
352 } |
|
353 } |
|
354 patched[*patched_len] = 0; |
|
355 #ifndef DGLES2_ALLOW_GLSL_110 |
|
356 /* add version directive is one was not found */ |
|
357 if (!version_found) { |
|
358 char* new_patched; |
|
359 *patched_len += strlen("#version 120\n"); |
|
360 new_patched = malloc(*patched_len + 1); |
|
361 if (new_patched == NULL) { |
|
362 return NULL; |
|
363 } |
|
364 strcpy(new_patched, "#version 120\n"); |
|
365 strcat(new_patched, patched); |
|
366 free(patched); |
|
367 patched = new_patched; |
|
368 } |
|
369 #endif // !DGLES2_ALLOW_GLSL_110 |
|
370 { |
|
371 /* check that we don't leave dummy preprocessor lines */ |
|
372 char *sp; |
|
373 for (sp = patched; *sp;) { |
|
374 for (; *sp == ' ' || *sp == '\t'; sp++); |
|
375 if (!strncmp(sp, "#define", 7)) { |
|
376 for (p = sp + 7; *p == ' ' || *p == '\t'; p++); |
|
377 if (*p == '\n' || *p == '\r' || *p == '/') { |
|
378 memset(sp, 0x20, 7); |
|
379 } |
|
380 } |
|
381 for (; *sp && *sp != '\n' && *sp != '\r'; sp++); |
|
382 for (; *sp == '\n' || *sp == '\r'; sp++); |
|
383 } |
|
384 } |
|
385 |
|
386 return patched; |
|
387 } |
|
388 } |
|
389 |
|
390 GL_APICALL_BUILD void GL_APIENTRY glShaderSource(GLuint shader, GLsizei count, const char** string, const GLint* length) |
|
391 { |
|
392 DGLES2_ENTER(); |
|
393 DGLES2_ERROR_IF(ctx->hgl.IsProgram(shader), GL_INVALID_OPERATION); |
|
394 DGLES2_ERROR_IF(!ctx->hgl.IsShader(shader), GL_INVALID_VALUE); |
|
395 DGLES2_ERROR_IF(count < 0, GL_INVALID_VALUE); |
|
396 /* |
|
397 #if(CONFIG_DEBUG == 1) |
|
398 Dprintf("Editing shader:\n--ORIGINAL-SHADER--\n"); |
|
399 for(unsigned i = 0; i < count; ++i) |
|
400 { |
|
401 if(length) |
|
402 Dprintf("%*s", length[i], string[i]); |
|
403 else |
|
404 Dprintf("%s", string[i]); |
|
405 } |
|
406 Dprintf("---END-ORIGINAL-SHADER---\n"); |
|
407 #endif // !NDEBUG |
|
408 char** string_dgl = malloc(sizeof(char*)*count); |
|
409 GLint* length_dgl = malloc(sizeof(GLint)*count); |
|
410 |
|
411 // Remove the non OpenGL 2.x compilant keywords. |
|
412 for(unsigned i = 0; i < count; ++i) |
|
413 { |
|
414 static const char* removables[] = |
|
415 { |
|
416 "precision highp float;", |
|
417 "precision mediump float;", |
|
418 "precision lowp float;", |
|
419 "highp", |
|
420 "lowp", |
|
421 "mediump", |
|
422 "precision" |
|
423 }; |
|
424 |
|
425 length_dgl[i] = length ? length[i] : strlen(string[i]); |
|
426 string_dgl[i] = malloc(length_dgl[i] + 1); |
|
427 memcpy(string_dgl[i], string[i], length_dgl[i]); |
|
428 string_dgl[i][length_dgl[i]] = 0; |
|
429 |
|
430 for(unsigned j = 0; j < sizeof(removables)/sizeof(removables[0]); ++j) |
|
431 { |
|
432 char const* p; |
|
433 while((p = strstr(string_dgl[i], removables[j]))) |
|
434 { |
|
435 memmove(p, p + strlen(removables[j]), strlen(p + strlen(removables[j])) + 1); |
|
436 } |
|
437 } |
|
438 } |
|
439 #if(CONFIG_DEBUG == 1) |
|
440 Dprintf("Loading shader:\n--DESKTOP-GL-SHADER--\n"); |
|
441 for(unsigned i = 0; i < count; ++i) |
|
442 { |
|
443 Dprintf("%*s", length_dgl[i], string_dgl[i]); |
|
444 } |
|
445 Dprintf("---END-DESKTOP-GL-SHADER---\n"); |
|
446 #endif // !NDEBUG |
|
447 |
|
448 ctx->hgl.ShaderSource(shader, count, string_dgl, length_dgl); |
|
449 |
|
450 for(unsigned i = 0; i < count; ++i) |
|
451 free(string_dgl[i]); |
|
452 free(string_dgl); |
|
453 free(length_dgl); |
|
454 */ |
|
455 |
|
456 if(count > 0 && string != NULL) |
|
457 { |
|
458 char* source = NULL; |
|
459 int total_len = 0; |
|
460 |
|
461 if(count > 1) |
|
462 { |
|
463 int i; |
|
464 |
|
465 // Concatenate the passed strings into one source string. |
|
466 for(i = 0; i < count; i++) |
|
467 { |
|
468 int len; |
|
469 |
|
470 if(string[i] == NULL) |
|
471 { |
|
472 continue; |
|
473 } |
|
474 |
|
475 if(length == NULL || length[i] < 0) |
|
476 { |
|
477 len = strlen(string[i]); |
|
478 } |
|
479 else |
|
480 { |
|
481 len = length[i]; |
|
482 } |
|
483 |
|
484 if(len > 0) |
|
485 { |
|
486 total_len += len; |
|
487 |
|
488 if(source == NULL) |
|
489 { |
|
490 source = malloc(total_len + 1); |
|
491 if(source == NULL) |
|
492 { |
|
493 DGLES2_ERROR(GL_OUT_OF_MEMORY); |
|
494 } |
|
495 source[0] = 0; |
|
496 } |
|
497 else |
|
498 { |
|
499 source = realloc(source, total_len + 1); |
|
500 if(source == NULL) |
|
501 { |
|
502 DGLES2_ERROR(GL_OUT_OF_MEMORY); |
|
503 } |
|
504 } |
|
505 |
|
506 strncat(source, string[i], len); |
|
507 } |
|
508 } |
|
509 } |
|
510 else |
|
511 { |
|
512 source = (char*)string[0]; |
|
513 if(length == NULL || length[0] < 0) |
|
514 { |
|
515 total_len = strlen(source); |
|
516 } |
|
517 else |
|
518 { |
|
519 total_len = length[0]; |
|
520 } |
|
521 } |
|
522 |
|
523 { |
|
524 // FIXME: This will fail with real constant data! |
|
525 int patched_len; |
|
526 const GLchar* patched = do_eglShaderPatch(source, total_len, &patched_len); |
|
527 if(patched == NULL) |
|
528 { |
|
529 DGLES2_ERROR(GL_OUT_OF_MEMORY); |
|
530 } |
|
531 ctx->hgl.ShaderSource(shader, 1, &patched, &patched_len); |
|
532 free((void*)patched); |
|
533 } |
|
534 |
|
535 if(DGLContext_getHostError(ctx) == GL_NO_ERROR) |
|
536 { |
|
537 if(!DGLContext_setShaderSource(ctx, shader, source, total_len)) |
|
538 { |
|
539 DGLES2_ERROR(GL_OUT_OF_MEMORY); |
|
540 } |
|
541 } |
|
542 |
|
543 if(count > 1) |
|
544 { |
|
545 free(source); |
|
546 } |
|
547 } |
|
548 else |
|
549 { |
|
550 ctx->hgl.ShaderSource(shader, count, string, length); |
|
551 } |
|
552 |
|
553 DGLES2_LEAVE(); |
|
554 } |