Merge branch 'mesa_7_5_branch'
[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 (!feof(f)) {
496 fgets(line, sizeof(line), f);
497 if (!feof(f) && line[0]) {
498 if (strncmp(line, "vs ", 3) == 0) {
499 VertShaderFile = strdup(line + 3);
500 VertShaderFile[strlen(VertShaderFile) - 1] = 0;
501 }
502 else if (strncmp(line, "fs ", 3) == 0) {
503 FragShaderFile = strdup(line + 3);
504 FragShaderFile[strlen(FragShaderFile) - 1] = 0;
505 }
506 else if (strncmp(line, "texture ", 8) == 0) {
507 char target[100], texFileName[100];
508 int unit, k;
509 k = sscanf(line + 8, "%d %s %s", &unit, target, texFileName);
510 assert(k == 3 || k == 8);
511 if (strcmp(target, "CUBE") == 0) {
512 char texFileNames[6][100];
513 k = sscanf(line + 8, "%d %s %s %s %s %s %s %s",
514 &unit, target,
515 texFileNames[0],
516 texFileNames[1],
517 texFileNames[2],
518 texFileNames[3],
519 texFileNames[4],
520 texFileNames[5]);
521 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texFileNames[0]);
522 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, texFileNames[1]);
523 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, texFileNames[2]);
524 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, texFileNames[3]);
525 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, texFileNames[4]);
526 LoadTexture(unit, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, texFileNames[5]);
527 }
528 else if (!strcmp(target, "2D")) {
529 LoadTexture(unit, GL_TEXTURE_2D, texFileName);
530 }
531 else if (!strcmp(target, "3D")) {
532 LoadTexture(unit, GL_TEXTURE_3D, texFileName);
533 }
534 else if (!strcmp(target, "RECT")) {
535 LoadTexture(unit, GL_TEXTURE_RECTANGLE_ARB, texFileName);
536 }
537 else {
538 printf("Bad texture target: %s\n", target);
539 exit(1);
540 }
541 }
542 else if (strncmp(line, "uniform ", 8) == 0) {
543 char name[1000], typeName[100];
544 int k;
545 float v1 = 0.0F, v2 = 0.0F, v3 = 0.0F, v4 = 0.0F;
546 GLenum type;
547
548 k = sscanf(line + 8, "%s %s %f %f %f %f", name, typeName,
549 &v1, &v2, &v3, &v4);
550
551 type = TypeFromName(typeName);
552
553 strcpy(conf->uniforms[conf->num_uniforms].name, name);
554 conf->uniforms[conf->num_uniforms].value[0] = v1;
555 conf->uniforms[conf->num_uniforms].value[1] = v2;
556 conf->uniforms[conf->num_uniforms].value[2] = v3;
557 conf->uniforms[conf->num_uniforms].value[3] = v4;
558 conf->uniforms[conf->num_uniforms].type = type;
559 conf->num_uniforms++;
560 }
561 else {
562 if (strlen(line) > 1) {
563 fprintf(stderr, "syntax error in: %s\n", line);
564 break;
565 }
566 }
567 }
568 }
569
570 fclose(f);
571 }
572
573
574 static void
575 Init(void)
576 {
577 GLdouble vertTime, fragTime, linkTime;
578 struct config_file config;
579
580 memset(&config, 0, sizeof(config));
581
582 if (ConfigFile)
583 ReadConfigFile(ConfigFile, &config);
584
585 if (!VertShaderFile) {
586 fprintf(stderr, "Error: no vertex shader\n");
587 exit(1);
588 }
589
590 if (!FragShaderFile) {
591 fprintf(stderr, "Error: no fragment shader\n");
592 exit(1);
593 }
594
595 if (!ShadersSupported())
596 exit(1);
597
598 vertShader = CompileShaderFile(GL_VERTEX_SHADER, VertShaderFile);
599 vertTime = GetShaderCompileTime();
600 fragShader = CompileShaderFile(GL_FRAGMENT_SHADER, FragShaderFile);
601 fragTime = GetShaderCompileTime();
602
603 Program = LinkShaders(vertShader, fragShader);
604 linkTime = GetShaderLinkTime();
605
606 printf("Read vert shader %s\n", VertShaderFile);
607 printf("Read frag shader %s\n", FragShaderFile);
608
609 printf("Time to compile vertex shader: %fs\n", vertTime);
610 printf("Time to compile fragment shader: %fs\n", fragTime);
611 printf("Time to link shaders: %fs\n", linkTime);
612
613 assert(ValidateShaderProgram(Program));
614
615 glUseProgram(Program);
616
617 NumUniforms = GetUniforms(Program, Uniforms);
618 if (config.num_uniforms) {
619 InitUniforms(&config, Uniforms);
620 }
621 else {
622 RandomUniformValues();
623 }
624 SetUniformValues(Program, Uniforms);
625 PrintUniforms(Uniforms);
626
627 NumAttribs = GetAttribs(Program, Attribs);
628 PrintAttribs(Attribs);
629
630 //assert(glGetError() == 0);
631
632 glClearColor(0.4f, 0.4f, 0.8f, 0.0f);
633
634 glEnable(GL_DEPTH_TEST);
635
636 glColor3f(1, 0, 0);
637 }
638
639
640 static void
641 Keys(void)
642 {
643 printf("Keyboard:\n");
644 printf(" a Animation toggle\n");
645 printf(" r Randomize uniform values\n");
646 printf(" o Change object\n");
647 printf(" arrows Rotate object\n");
648 printf(" ESC Exit\n");
649 }
650
651
652 static void
653 Usage(void)
654 {
655 printf("Usage:\n");
656 printf(" shtest config.shtest\n");
657 printf(" Run w/ given config file.\n");
658 printf(" shtest --vs vertShader --fs fragShader\n");
659 printf(" Load/compile given shaders.\n");
660 }
661
662
663 static void
664 ParseOptions(int argc, char *argv[])
665 {
666 int i;
667
668 if (argc == 1) {
669 Usage();
670 exit(1);
671 }
672
673 for (i = 1; i < argc; i++) {
674 if (strcmp(argv[i], "--fs") == 0) {
675 FragShaderFile = argv[i+1];
676 i++;
677 }
678 else if (strcmp(argv[i], "--vs") == 0) {
679 VertShaderFile = argv[i+1];
680 i++;
681 }
682 else {
683 /* assume the arg is a config file */
684 ConfigFile = argv[i];
685 break;
686 }
687 }
688 }
689
690
691 int
692 main(int argc, char *argv[])
693 {
694 glutInitWindowSize(400, 400);
695 glutInit(&argc, argv);
696 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
697 win = glutCreateWindow(argv[0]);
698 glewInit();
699 glutReshapeFunc(Reshape);
700 glutKeyboardFunc(Key);
701 glutSpecialFunc(SpecialKey);
702 glutDisplayFunc(Redisplay);
703 ParseOptions(argc, argv);
704 Init();
705 Keys();
706 glutMainLoop();
707 return 0;
708 }
709