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