progs/glsl: Prevent possible string overflow.
[mesa.git] / progs / glsl / shtest.c
1 /*
2 * Simple shader test harness.
3 * Brian Paul
4 * 13 Aug 2009
5 *
6 * Usage:
7 * shtest --vs vertShaderFile --fs fragShaderFile
8 *
9 * In this case the given vertex/frag shaders are read and compiled.
10 * Random values are assigned to the uniforms.
11 *
12 * or:
13 * shtest configFile
14 *
15 * In this case a config file is read that specifies the file names
16 * of the shaders plus initial values for uniforms.
17 *
18 * Example config file:
19 *
20 * vs shader.vert
21 * fs shader.frag
22 * uniform pi 3.14159
23 * uniform v1 1.0 0.5 0.2 0.3
24 * texture 0 2D texture0.rgb
25 * texture 1 CUBE texture1.rgb
26 * texture 2 RECT texture2.rgb
27 *
28 */
29
30
31 #include <assert.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <math.h>
37 #include <GL/glew.h>
38 #include <GL/glu.h>
39 #include <GL/glut.h>
40 #include "shaderutil.h"
41 #include "readtex.h"
42
43
44 typedef enum
45 {
46 SPHERE,
47 CUBE,
48 NUM_SHAPES
49 } shape;
50
51
52 static char *FragShaderFile = NULL;
53 static char *VertShaderFile = NULL;
54 static char *ConfigFile = NULL;
55
56 /* program/shader objects */
57 static GLuint fragShader;
58 static GLuint vertShader;
59 static GLuint Program;
60
61
62 #define MAX_UNIFORMS 100
63 static struct uniform_info Uniforms[MAX_UNIFORMS];
64 static GLuint NumUniforms = 0;
65
66
67 #define MAX_ATTRIBS 100
68 static struct attrib_info Attribs[MAX_ATTRIBS];
69 static GLuint NumAttribs = 0;
70
71
72 /**
73 * Config file info.
74 */
75 struct config_file
76 {
77 struct name_value
78 {
79 char name[100];
80 float value[4];
81 int type;
82 } uniforms[100];
83
84 int num_uniforms;
85 };
86
87
88 static GLint win = 0;
89 static GLboolean Anim = GL_FALSE;
90 static GLfloat TexRot = 0.0;
91 static GLfloat xRot = 0.0f, yRot = 0.0f, zRot = 0.0f;
92 static shape Object = SPHERE;
93
94
95 static float
96 RandomFloat(float min, float max)
97 {
98 int k = rand() % 10000;
99 float x = min + (max - min) * k / 10000.0;
100 return x;
101 }
102
103
104 /** Set new random values for uniforms */
105 static void
106 RandomUniformValues(void)
107 {
108 GLuint i;
109 for (i = 0; i < NumUniforms; i++) {
110 switch (Uniforms[i].type) {
111 case GL_FLOAT:
112 Uniforms[i].value[0] = RandomFloat(0.0, 1.0);
113 break;
114 case GL_SAMPLER_1D:
115 case GL_SAMPLER_2D:
116 case GL_SAMPLER_3D:
117 case GL_SAMPLER_CUBE:
118 case GL_SAMPLER_2D_RECT_ARB:
119 /* don't change sampler values - random values are bad */
120 break;
121 default:
122 Uniforms[i].value[0] = RandomFloat(-1.0, 2.0);
123 Uniforms[i].value[1] = RandomFloat(-1.0, 2.0);
124 Uniforms[i].value[2] = RandomFloat(-1.0, 2.0);
125 Uniforms[i].value[3] = RandomFloat(-1.0, 2.0);
126 }
127 }
128 }
129
130
131 static void
132 Idle(void)
133 {
134 yRot += 2.0;
135 if (yRot > 360.0)
136 yRot -= 360.0;
137 glutPostRedisplay();
138 }
139
140
141
142 static void
143 SquareVertex(GLfloat s, GLfloat t, GLfloat size)
144 {
145 GLfloat x = -size + s * 2.0 * size;
146 GLfloat y = -size + t * 2.0 * size;
147 GLuint i;
148
149 glMultiTexCoord2f(GL_TEXTURE0, s, t);
150 glMultiTexCoord2f(GL_TEXTURE1, s, t);
151 glMultiTexCoord2f(GL_TEXTURE2, s, t);
152 glMultiTexCoord2f(GL_TEXTURE3, s, t);
153
154 /* assign (s,t) to the generic attributes */
155 for (i = 0; i < NumAttribs; i++) {
156 if (Attribs[i].location >= 0) {
157 glVertexAttrib2f(Attribs[i].location, s, t);
158 }
159 }
160
161 glVertex2f(x, y);
162 }
163
164
165 /*
166 * Draw a square, specifying normal and tangent vectors.
167 */
168 static void
169 Square(GLfloat size)
170 {
171 GLint tangentAttrib = 1;
172 glNormal3f(0, 0, 1);
173 glVertexAttrib3f(tangentAttrib, 1, 0, 0);
174 glBegin(GL_POLYGON);
175 #if 1
176 SquareVertex(0, 0, size);
177 SquareVertex(1, 0, size);
178 SquareVertex(1, 1, size);
179 SquareVertex(0, 1, size);
180 #else
181 glTexCoord2f(0, 0); glVertex2f(-size, -size);
182 glTexCoord2f(1, 0); glVertex2f( size, -size);
183 glTexCoord2f(1, 1); glVertex2f( size, size);
184 glTexCoord2f(0, 1); glVertex2f(-size, size);
185 #endif
186 glEnd();
187 }
188
189
190 static void
191 Cube(GLfloat size)
192 {
193 /* +X */
194 glPushMatrix();
195 glRotatef(90, 0, 1, 0);
196 glTranslatef(0, 0, size);
197 Square(size);
198 glPopMatrix();
199
200 /* -X */
201 glPushMatrix();
202 glRotatef(-90, 0, 1, 0);
203 glTranslatef(0, 0, size);
204 Square(size);
205 glPopMatrix();
206
207 /* +Y */
208 glPushMatrix();
209 glRotatef(90, 1, 0, 0);
210 glTranslatef(0, 0, size);
211 Square(size);
212 glPopMatrix();
213
214 /* -Y */
215 glPushMatrix();
216 glRotatef(-90, 1, 0, 0);
217 glTranslatef(0, 0, size);
218 Square(size);
219 glPopMatrix();
220
221
222 /* +Z */
223 glPushMatrix();
224 glTranslatef(0, 0, size);
225 Square(size);
226 glPopMatrix();
227
228 /* -Z */
229 glPushMatrix();
230 glRotatef(180, 0, 1, 0);
231 glTranslatef(0, 0, size);
232 Square(size);
233 glPopMatrix();
234 }
235
236
237 static void
238 Sphere(GLfloat radius, GLint slices, GLint stacks)
239 {
240 static GLUquadricObj *q = NULL;
241
242 if (!q) {
243 q = gluNewQuadric();
244 gluQuadricDrawStyle(q, GLU_FILL);
245 gluQuadricNormals(q, GLU_SMOOTH);
246 gluQuadricTexture(q, GL_TRUE);
247 }
248
249 gluSphere(q, radius, slices, stacks);
250 }
251
252
253 static void
254 Redisplay(void)
255 {
256 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
257
258 glPushMatrix();
259 glRotatef(xRot, 1.0f, 0.0f, 0.0f);
260 glRotatef(yRot, 0.0f, 1.0f, 0.0f);
261 glRotatef(zRot, 0.0f, 0.0f, 1.0f);
262
263 glMatrixMode(GL_TEXTURE);
264 glLoadIdentity();
265 glRotatef(TexRot, 0.0f, 1.0f, 0.0f);
266 glMatrixMode(GL_MODELVIEW);
267
268 if (Object == SPHERE) {
269 Sphere(2.5, 20, 10);
270 }
271 else if (Object == CUBE) {
272 Cube(2.0);
273 }
274
275 glPopMatrix();
276
277 glutSwapBuffers();
278 }
279
280
281 static void
282 Reshape(int width, int height)
283 {
284 glViewport(0, 0, width, height);
285 glMatrixMode(GL_PROJECTION);
286 glLoadIdentity();
287 glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
288 glMatrixMode(GL_MODELVIEW);
289 glLoadIdentity();
290 glTranslatef(0.0f, 0.0f, -15.0f);
291 }
292
293
294 static void
295 CleanUp(void)
296 {
297 glDeleteShader(fragShader);
298 glDeleteShader(vertShader);
299 glDeleteProgram(Program);
300 glutDestroyWindow(win);
301 }
302
303
304 static void
305 Key(unsigned char key, int x, int y)
306 {
307 const GLfloat step = 2.0;
308 (void) x;
309 (void) y;
310
311 switch(key) {
312 case 'a':
313 Anim = !Anim;
314 if (Anim)
315 glutIdleFunc(Idle);
316 else
317 glutIdleFunc(NULL);
318 break;
319 case 'z':
320 zRot += step;
321 break;
322 case 'Z':
323 zRot -= step;
324 break;
325 case 'o':
326 Object = (Object + 1) % NUM_SHAPES;
327 break;
328 case 'r':
329 RandomUniformValues();
330 SetUniformValues(Program, Uniforms);
331 PrintUniforms(Uniforms);
332 break;
333 case 27:
334 CleanUp();
335 exit(0);
336 break;
337 }
338 glutPostRedisplay();
339 }
340
341
342 static void
343 SpecialKey(int key, int x, int y)
344 {
345 const GLfloat step = 2.0;
346
347 (void) x;
348 (void) y;
349
350 switch(key) {
351 case GLUT_KEY_UP:
352 xRot += step;
353 break;
354 case GLUT_KEY_DOWN:
355 xRot -= step;
356 break;
357 case GLUT_KEY_LEFT:
358 yRot -= step;
359 break;
360 case GLUT_KEY_RIGHT:
361 yRot += step;
362 break;
363 }
364 glutPostRedisplay();
365 }
366
367
368 static void
369 InitUniforms(const struct config_file *conf,
370 struct uniform_info uniforms[])
371 {
372 int i;
373
374 for (i = 0; i < conf->num_uniforms; i++) {
375 int j;
376 for (j = 0; uniforms[j].name; j++) {
377 if (strcmp(uniforms[j].name, conf->uniforms[i].name) == 0) {
378 uniforms[j].type = conf->uniforms[i].type;
379 uniforms[j].value[0] = conf->uniforms[i].value[0];
380 uniforms[j].value[1] = conf->uniforms[i].value[1];
381 uniforms[j].value[2] = conf->uniforms[i].value[2];
382 uniforms[j].value[3] = conf->uniforms[i].value[3];
383 }
384 }
385 }
386 }
387
388
389 static void
390 LoadTexture(GLint unit, GLenum target, const char *texFileName)
391 {
392 GLint imgWidth, imgHeight;
393 GLenum imgFormat;
394 GLubyte *image = NULL;
395 GLuint tex;
396 GLenum filter = GL_LINEAR;
397 GLenum objTarget;
398
399 image = LoadRGBImage(texFileName, &imgWidth, &imgHeight, &imgFormat);
400 if (!image) {
401 printf("Couldn't read %s\n", texFileName);
402 exit(1);
403 }
404
405 printf("Load Texture: unit %d, target 0x%x: %s %d x %d\n",
406 unit, target, texFileName, imgWidth, imgHeight);
407
408 if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
409 target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
410 objTarget = GL_TEXTURE_CUBE_MAP;
411 }
412 else {
413 objTarget = target;
414 }
415
416 glActiveTexture(GL_TEXTURE0 + unit);
417 glGenTextures(1, &tex);
418 glBindTexture(objTarget, tex);
419
420 if (target == GL_TEXTURE_3D) {
421 /* depth=1 */
422 gluBuild3DMipmaps(target, 4, imgWidth, imgHeight, 1,
423 imgFormat, GL_UNSIGNED_BYTE, image);
424 }
425 else if (target == GL_TEXTURE_1D) {
426 gluBuild1DMipmaps(target, 4, imgWidth,
427 imgFormat, GL_UNSIGNED_BYTE, image);
428 }
429 else {
430 gluBuild2DMipmaps(target, 4, imgWidth, imgHeight,
431 imgFormat, GL_UNSIGNED_BYTE, image);
432 }
433
434 free(image);
435
436 glTexParameteri(objTarget, GL_TEXTURE_WRAP_S, GL_REPEAT);
437 glTexParameteri(objTarget, GL_TEXTURE_WRAP_T, GL_REPEAT);
438 glTexParameteri(objTarget, GL_TEXTURE_MIN_FILTER, filter);
439 glTexParameteri(objTarget, GL_TEXTURE_MAG_FILTER, filter);
440 }
441
442
443 static GLenum
444 TypeFromName(const char *n)
445 {
446 static const struct {
447 const char *name;
448 GLenum type;
449 } types[] = {
450 { "GL_FLOAT", GL_FLOAT },
451 { "GL_FLOAT_VEC2", GL_FLOAT_VEC2 },
452 { "GL_FLOAT_VEC3", GL_FLOAT_VEC3 },
453 { "GL_FLOAT_VEC4", GL_FLOAT_VEC4 },
454 { "GL_INT", GL_INT },
455 { "GL_INT_VEC2", GL_INT_VEC2 },
456 { "GL_INT_VEC3", GL_INT_VEC3 },
457 { "GL_INT_VEC4", GL_INT_VEC4 },
458 { "GL_SAMPLER_1D", GL_SAMPLER_1D },
459 { "GL_SAMPLER_2D", GL_SAMPLER_2D },
460 { "GL_SAMPLER_3D", GL_SAMPLER_3D },
461 { "GL_SAMPLER_CUBE", GL_SAMPLER_CUBE },
462 { "GL_SAMPLER_2D_RECT", GL_SAMPLER_2D_RECT_ARB },
463 { NULL, 0 }
464 };
465 GLuint i;
466
467 for (i = 0; types[i].name; i++) {
468 if (strcmp(types[i].name, n) == 0)
469 return types[i].type;
470 }
471 abort();
472 return GL_NONE;
473 }
474
475
476
477 /**
478 * Read a config file.
479 */
480 static void
481 ReadConfigFile(const char *filename, struct config_file *conf)
482 {
483 char line[1000];
484 FILE *f;
485
486 f = fopen(filename, "r");
487 if (!f) {
488 fprintf(stderr, "Unable to open config file %s\n", filename);
489 exit(1);
490 }
491
492 conf->num_uniforms = 0;
493
494 /* ugly but functional parser */
495 while (fgets(line, sizeof(line), f) != NULL) {
496 if (line[0]) {
497 if (strncmp(line, "vs ", 3) == 0) {
498 VertShaderFile = strdup(line + 3);
499 VertShaderFile[strlen(VertShaderFile) - 1] = 0;
500 }
501 else if (strncmp(line, "fs ", 3) == 0) {
502 FragShaderFile = strdup(line + 3);
503 FragShaderFile[strlen(FragShaderFile) - 1] = 0;
504 }
505 else if (strncmp(line, "texture ", 8) == 0) {
506 char target[100], texFileName[100];
507 int unit, k;
508 k = sscanf(line + 8, "%d %s %s", &unit, target, texFileName);
509 assert(k == 3 || k == 8);
510 if (strcmp(target, "CUBE") == 0) {
511 char texFileNames[6][100];
512 k = sscanf(line + 8, "%d %s %s %s %s %s %s %s",
513 &unit, target,
514 texFileNames[0],
515 texFileNames[1],
516 texFileNames[2],
517 texFileNames[3],
518 texFileNames[4],
519 texFileNames[5]);
520 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texFileNames[0]);
521 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, texFileNames[1]);
522 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, texFileNames[2]);
523 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, texFileNames[3]);
524 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, texFileNames[4]);
525 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, texFileNames[5]);
526 }
527 else if (!strcmp(target, "2D")) {
528 LoadTexture(unit, GL_TEXTURE_2D, texFileName);
529 }
530 else if (!strcmp(target, "3D")) {
531 LoadTexture(unit, GL_TEXTURE_3D, texFileName);
532 }
533 else if (!strcmp(target, "RECT")) {
534 LoadTexture(unit, GL_TEXTURE_RECTANGLE_ARB, texFileName);
535 }
536 else {
537 printf("Bad texture target: %s\n", target);
538 exit(1);
539 }
540 }
541 else if (strncmp(line, "uniform ", 8) == 0) {
542 char name[1000], typeName[100];
543 int k;
544 float v1 = 0.0F, v2 = 0.0F, v3 = 0.0F, v4 = 0.0F;
545 GLenum type;
546
547 k = sscanf(line + 8, "%s %s %f %f %f %f", name, typeName,
548 &v1, &v2, &v3, &v4);
549
550 type = TypeFromName(typeName);
551
552 if (strlen(name) + 1 > sizeof(conf->uniforms[conf->num_uniforms].name)) {
553 fprintf(stderr, "string overflow\n");
554 exit(1);
555 }
556 strcpy(conf->uniforms[conf->num_uniforms].name, name);
557 conf->uniforms[conf->num_uniforms].value[0] = v1;
558 conf->uniforms[conf->num_uniforms].value[1] = v2;
559 conf->uniforms[conf->num_uniforms].value[2] = v3;
560 conf->uniforms[conf->num_uniforms].value[3] = v4;
561 conf->uniforms[conf->num_uniforms].type = type;
562 conf->num_uniforms++;
563 }
564 else {
565 if (strlen(line) > 1) {
566 fprintf(stderr, "syntax error in: %s\n", line);
567 break;
568 }
569 }
570 }
571 }
572
573 fclose(f);
574 }
575
576
577 static void
578 Init(void)
579 {
580 GLdouble vertTime, fragTime, linkTime;
581 struct config_file config;
582
583 memset(&config, 0, sizeof(config));
584
585 if (ConfigFile)
586 ReadConfigFile(ConfigFile, &config);
587
588 if (!VertShaderFile) {
589 fprintf(stderr, "Error: no vertex shader\n");
590 exit(1);
591 }
592
593 if (!FragShaderFile) {
594 fprintf(stderr, "Error: no fragment shader\n");
595 exit(1);
596 }
597
598 if (!ShadersSupported())
599 exit(1);
600
601 vertShader = CompileShaderFile(GL_VERTEX_SHADER, VertShaderFile);
602 vertTime = GetShaderCompileTime();
603 fragShader = CompileShaderFile(GL_FRAGMENT_SHADER, FragShaderFile);
604 fragTime = GetShaderCompileTime();
605
606 Program = LinkShaders(vertShader, fragShader);
607 linkTime = GetShaderLinkTime();
608
609 printf("Read vert shader %s\n", VertShaderFile);
610 printf("Read frag shader %s\n", FragShaderFile);
611
612 printf("Time to compile vertex shader: %fs\n", vertTime);
613 printf("Time to compile fragment shader: %fs\n", fragTime);
614 printf("Time to link shaders: %fs\n", linkTime);
615
616 assert(ValidateShaderProgram(Program));
617
618 glUseProgram(Program);
619
620 NumUniforms = GetUniforms(Program, Uniforms);
621 if (config.num_uniforms) {
622 InitUniforms(&config, Uniforms);
623 }
624 else {
625 RandomUniformValues();
626 }
627 SetUniformValues(Program, Uniforms);
628 PrintUniforms(Uniforms);
629
630 NumAttribs = GetAttribs(Program, Attribs);
631 PrintAttribs(Attribs);
632
633 /* assert(glGetError() == 0); */
634
635 glClearColor(0.4f, 0.4f, 0.8f, 0.0f);
636
637 glEnable(GL_DEPTH_TEST);
638
639 glColor3f(1, 0, 0);
640 }
641
642
643 static void
644 Keys(void)
645 {
646 printf("Keyboard:\n");
647 printf(" a Animation toggle\n");
648 printf(" r Randomize uniform values\n");
649 printf(" o Change object\n");
650 printf(" arrows Rotate object\n");
651 printf(" ESC Exit\n");
652 }
653
654
655 static void
656 Usage(void)
657 {
658 printf("Usage:\n");
659 printf(" shtest config.shtest\n");
660 printf(" Run w/ given config file.\n");
661 printf(" shtest --vs vertShader --fs fragShader\n");
662 printf(" Load/compile given shaders.\n");
663 }
664
665
666 static void
667 ParseOptions(int argc, char *argv[])
668 {
669 int i;
670
671 if (argc == 1) {
672 Usage();
673 exit(1);
674 }
675
676 for (i = 1; i < argc; i++) {
677 if (strcmp(argv[i], "--fs") == 0) {
678 FragShaderFile = argv[i+1];
679 i++;
680 }
681 else if (strcmp(argv[i], "--vs") == 0) {
682 VertShaderFile = argv[i+1];
683 i++;
684 }
685 else {
686 /* assume the arg is a config file */
687 ConfigFile = argv[i];
688 break;
689 }
690 }
691 }
692
693
694 int
695 main(int argc, char *argv[])
696 {
697 glutInitWindowSize(400, 400);
698 glutInit(&argc, argv);
699 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
700 win = glutCreateWindow(argv[0]);
701 glewInit();
702 glutReshapeFunc(Reshape);
703 glutKeyboardFunc(Key);
704 glutSpecialFunc(SpecialKey);
705 glutDisplayFunc(Redisplay);
706 ParseOptions(argc, argv);
707 Init();
708 Keys();
709 glutMainLoop();
710 return 0;
711 }
712