17ec3b29aSmrg/**
27ec3b29aSmrg * Test compilation, link, draw time for very large shaders.
37ec3b29aSmrg * Command line arguments:
47ec3b29aSmrg *  -v    verbose output (print shader code)
57ec3b29aSmrg *  -c N  generate shaders of complexity N
67ec3b29aSmrg *  -n N  generate and draw with N shader programs
77ec3b29aSmrg *
87ec3b29aSmrg * Brian Paul
97ec3b29aSmrg * 3 Dec 2015
107ec3b29aSmrg */
117ec3b29aSmrg
127ec3b29aSmrg
137ec3b29aSmrg#include <assert.h>
147ec3b29aSmrg#include <stdio.h>
157ec3b29aSmrg#include <stdbool.h>
167ec3b29aSmrg#include <stdlib.h>
177ec3b29aSmrg#include <string.h>
187ec3b29aSmrg#include <math.h>
197ec3b29aSmrg#include <GL/glew.h>
207ec3b29aSmrg#include "glut_wrap.h"
217ec3b29aSmrg#include "shaderutil.h"
227ec3b29aSmrg
237ec3b29aSmrg#if defined(_MSC_VER)
247ec3b29aSmrg#define snprintf _snprintf
257ec3b29aSmrg#endif
267ec3b29aSmrg
277ec3b29aSmrg
287ec3b29aSmrg#define MAX_PROGRAMS 10000
297ec3b29aSmrg
307ec3b29aSmrgstatic int Win;
317ec3b29aSmrgstatic int WinWidth = 400, WinHeight = 400;
327ec3b29aSmrgstatic int verbose = 0;
337ec3b29aSmrgstatic int num_shaders = 1;
347ec3b29aSmrgstatic int complexity = 5;
357ec3b29aSmrgstatic int programs[MAX_PROGRAMS];
367ec3b29aSmrg
377ec3b29aSmrgstatic const float u1_val[4] = {.1, .2, .3, .4};
387ec3b29aSmrgstatic const float u2_val[4] = {.6, .7, .8, .9};
397ec3b29aSmrg
407ec3b29aSmrgstatic const char *VS_code =
417ec3b29aSmrg   "void main()\n"
427ec3b29aSmrg   "{\n"
437ec3b29aSmrg   "  gl_Position = ftransform();\n"
447ec3b29aSmrg   "}\n";
457ec3b29aSmrg
467ec3b29aSmrgstatic const float coords[3][4] = {
477ec3b29aSmrg   {0, 0.1, 0, 1},
487ec3b29aSmrg   {0, 0, 0, 1},
497ec3b29aSmrg   {0, -0.1, 0, 1}
507ec3b29aSmrg};
517ec3b29aSmrg
527ec3b29aSmrg#define NUM_POINTS (sizeof(coords) / sizeof(coords[0]))
537ec3b29aSmrg
547ec3b29aSmrg
557ec3b29aSmrg
567ec3b29aSmrgstruct dynamic_string {
577ec3b29aSmrg   char *buffer;
587ec3b29aSmrg   unsigned len;
597ec3b29aSmrg   unsigned buffer_size;
607ec3b29aSmrg};
617ec3b29aSmrg
627ec3b29aSmrg
637ec3b29aSmrgstatic void
647ec3b29aSmrgappend_string(struct dynamic_string *ds, const char *s)
657ec3b29aSmrg{
667ec3b29aSmrg   int l = strlen(s);
677ec3b29aSmrg   if (ds->len + l >= ds->buffer_size) {
687ec3b29aSmrg      /* grow buffer */
697ec3b29aSmrg      int newsize = ds->buffer_size + l + 4096;
707ec3b29aSmrg      char *newbuf = malloc(newsize);
717ec3b29aSmrg      assert(newbuf);
727ec3b29aSmrg      if (ds->buffer)
737ec3b29aSmrg         strcpy(newbuf, ds->buffer);
747ec3b29aSmrg      free(ds->buffer);
757ec3b29aSmrg      ds->buffer = newbuf;
767ec3b29aSmrg      ds->buffer_size = newsize;
777ec3b29aSmrg   }
787ec3b29aSmrg   strcpy(ds->buffer + ds->len, s);
797ec3b29aSmrg   ds->len += l;
807ec3b29aSmrg   assert(strlen(ds->buffer) == ds->len);
817ec3b29aSmrg}
827ec3b29aSmrg
837ec3b29aSmrg
847ec3b29aSmrg/**
857ec3b29aSmrg * Index is a term put into the shader code to make each shader a little
867ec3b29aSmrg * different.
877ec3b29aSmrg */
887ec3b29aSmrgstatic char *
897ec3b29aSmrggen_large_shader(int num_functions, int index)
907ec3b29aSmrg{
917ec3b29aSmrg   int i;
927ec3b29aSmrg   struct dynamic_string ds = {0};
937ec3b29aSmrg   char s[100];
947ec3b29aSmrg
957ec3b29aSmrg   append_string(&ds, "#version 120\n");
967ec3b29aSmrg   append_string(&ds, "\nuniform vec4 u1, u2;\n\n");
977ec3b29aSmrg
987ec3b29aSmrg   for (i = 0; i < num_functions; i++) {
997ec3b29aSmrg      snprintf(s, sizeof(s), "vec4 func%d(vec4 a, float b)\n", i);
1007ec3b29aSmrg      append_string(&ds, s);
1017ec3b29aSmrg      append_string(&ds, "{\n");
1027ec3b29aSmrg      if (i == 0) {
1037ec3b29aSmrg         append_string(&ds, "   return a * b;\n");
1047ec3b29aSmrg      }
1057ec3b29aSmrg      else {
1067ec3b29aSmrg         snprintf(s, sizeof(s),
1077ec3b29aSmrg                  "   vec4 s = a * func%d(a, float(%d)) + vec4(b);\n",
1087ec3b29aSmrg                  i-1, index);
1097ec3b29aSmrg         append_string(&ds, s);
1107ec3b29aSmrg
1117ec3b29aSmrg         snprintf(s, sizeof(s),
1127ec3b29aSmrg                  "   vec4 t = a / func%d(a, 3.0) - vec4(b);\n", i-1);
1137ec3b29aSmrg         append_string(&ds, s);
1147ec3b29aSmrg
1157ec3b29aSmrg         if (i & 1) {
1167ec3b29aSmrg            append_string(&ds, "   vec4 u = max(s, t);\n");
1177ec3b29aSmrg         }
1187ec3b29aSmrg         else {
1197ec3b29aSmrg            /* use a conditional */
1207ec3b29aSmrg            append_string(&ds, "   vec4 u = min(s, t);\n");
1217ec3b29aSmrg            append_string(&ds, "   if (s.x > t.x) {\n");
1227ec3b29aSmrg            snprintf(s, sizeof(s), "      u = vec4(%d);\n", i);
1237ec3b29aSmrg            append_string(&ds, s);
1247ec3b29aSmrg            append_string(&ds, "   }\n");
1257ec3b29aSmrg         }
1267ec3b29aSmrg
1277ec3b29aSmrg         append_string(&ds, "   return u;\n");
1287ec3b29aSmrg      }
1297ec3b29aSmrg
1307ec3b29aSmrg      append_string(&ds, "}\n\n");
1317ec3b29aSmrg   }
1327ec3b29aSmrg
1337ec3b29aSmrg   append_string(&ds, "void main()\n");
1347ec3b29aSmrg   append_string(&ds, "{\n");
1357ec3b29aSmrg   snprintf(s, sizeof(s), "   gl_FragColor = func%d(u1, u2.x);\n", i-1);
1367ec3b29aSmrg   append_string(&ds, s);
1377ec3b29aSmrg   append_string(&ds, "}\n");
1387ec3b29aSmrg
1397ec3b29aSmrg   return ds.buffer;
1407ec3b29aSmrg}
1417ec3b29aSmrg
1427ec3b29aSmrg
1437ec3b29aSmrgstatic void
1447ec3b29aSmrgDraw(void)
1457ec3b29aSmrg{
1467ec3b29aSmrg   int t0, t1;
1477ec3b29aSmrg   int i;
1487ec3b29aSmrg   int fixed_func_time = 0, glsl_time_1 = 0, glsl_time_2 = 0;
1497ec3b29aSmrg
1507ec3b29aSmrg   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1517ec3b29aSmrg
1527ec3b29aSmrg   for (i = 0; i < num_shaders; i++) {
1537ec3b29aSmrg      UseProgram(0);
1547ec3b29aSmrg      t0 = glutGet(GLUT_ELAPSED_TIME);
1557ec3b29aSmrg      glDrawArrays(GL_POINTS, 0, NUM_POINTS);
1567ec3b29aSmrg      glFinish();
1577ec3b29aSmrg      t1 = glutGet(GLUT_ELAPSED_TIME);
1587ec3b29aSmrg      fixed_func_time += t1 - t0;
1597ec3b29aSmrg
1607ec3b29aSmrg      UseProgram(programs[i]);
1617ec3b29aSmrg      t0 = glutGet(GLUT_ELAPSED_TIME);
1627ec3b29aSmrg      glDrawArrays(GL_POINTS, 0, NUM_POINTS);
1637ec3b29aSmrg      glFinish();
1647ec3b29aSmrg      t1 = glutGet(GLUT_ELAPSED_TIME);
1657ec3b29aSmrg      glsl_time_1 += t1 - t0;
1667ec3b29aSmrg
1677ec3b29aSmrg      t0 = glutGet(GLUT_ELAPSED_TIME);
1687ec3b29aSmrg      glDrawArrays(GL_POINTS, 0, NUM_POINTS);
1697ec3b29aSmrg      glFinish();
1707ec3b29aSmrg      t1 = glutGet(GLUT_ELAPSED_TIME);
1717ec3b29aSmrg      glsl_time_2 += t1 - t0;
1727ec3b29aSmrg   }
1737ec3b29aSmrg
1747ec3b29aSmrg   printf("Time to draw fixed-function points: %d ms\n", fixed_func_time);
1757ec3b29aSmrg   printf("Time to draw 1st GLSL shader points: %d ms\n", glsl_time_1);
1767ec3b29aSmrg   printf("Time to draw 2st GLSL shader points: %d ms\n", glsl_time_2);
1777ec3b29aSmrg
1787ec3b29aSmrg   glutSwapBuffers();
1797ec3b29aSmrg}
1807ec3b29aSmrg
1817ec3b29aSmrg
1827ec3b29aSmrgstatic void
1837ec3b29aSmrgReshape(int width, int height)
1847ec3b29aSmrg{
1857ec3b29aSmrg   WinWidth = width;
1867ec3b29aSmrg   WinHeight = height;
1877ec3b29aSmrg   glViewport(0, 0, width, height);
1887ec3b29aSmrg   glMatrixMode(GL_PROJECTION);
1897ec3b29aSmrg   glLoadIdentity();
1907ec3b29aSmrg   glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
1917ec3b29aSmrg   glMatrixMode(GL_MODELVIEW);
1927ec3b29aSmrg   glLoadIdentity();
1937ec3b29aSmrg   glTranslatef(0.0, 0.0, -15.0);
1947ec3b29aSmrg}
1957ec3b29aSmrg
1967ec3b29aSmrg
1977ec3b29aSmrgstatic void
1987ec3b29aSmrgKey(unsigned char key, int x, int y)
1997ec3b29aSmrg{
2007ec3b29aSmrg   if (key == 27) {
2017ec3b29aSmrg      glutDestroyWindow(Win);
2027ec3b29aSmrg      exit(0);
2037ec3b29aSmrg   }
2047ec3b29aSmrg   glutPostRedisplay();
2057ec3b29aSmrg}
2067ec3b29aSmrg
2077ec3b29aSmrg
2087ec3b29aSmrgstatic GLuint
2097ec3b29aSmrgcreate_shader_program(const char *fs_code,
2107ec3b29aSmrg                      int *compile_time, int *link_time)
2117ec3b29aSmrg{
2127ec3b29aSmrg   GLuint fragShader;
2137ec3b29aSmrg   GLuint vertShader;
2147ec3b29aSmrg   GLuint program;
2157ec3b29aSmrg   GLint u1_loc, u2_loc;
2167ec3b29aSmrg   GLint t0, t1, t2;
2177ec3b29aSmrg
2187ec3b29aSmrg   vertShader = CompileShaderText(GL_VERTEX_SHADER, VS_code);
2197ec3b29aSmrg
2207ec3b29aSmrg   t0 = glutGet(GLUT_ELAPSED_TIME);
2217ec3b29aSmrg   fragShader = CompileShaderText(GL_FRAGMENT_SHADER, fs_code);
2227ec3b29aSmrg   t1 = glutGet(GLUT_ELAPSED_TIME);
2237ec3b29aSmrg   program = LinkShaders(vertShader, fragShader);
2247ec3b29aSmrg   t2 = glutGet(GLUT_ELAPSED_TIME);
2257ec3b29aSmrg
2267ec3b29aSmrg   UseProgram(program);
2277ec3b29aSmrg   u1_loc = glGetUniformLocation(program, "u1");
2287ec3b29aSmrg   u2_loc = glGetUniformLocation(program, "u2");
2297ec3b29aSmrg   glUniform4fv(u1_loc, 1, u1_val);
2307ec3b29aSmrg   glUniform4fv(u2_loc, 1, u2_val);
2317ec3b29aSmrg   UseProgram(0);
2327ec3b29aSmrg
2337ec3b29aSmrg   *compile_time = t1 - t0;
2347ec3b29aSmrg   *link_time = t2 - t1;
2357ec3b29aSmrg
2367ec3b29aSmrg   assert(glGetError() == GL_NO_ERROR);
2377ec3b29aSmrg
2387ec3b29aSmrg   return program;
2397ec3b29aSmrg}
2407ec3b29aSmrg
2417ec3b29aSmrg
2427ec3b29aSmrgstatic void
2437ec3b29aSmrgInit(void)
2447ec3b29aSmrg{
2457ec3b29aSmrg   GLuint vbo;
2467ec3b29aSmrg   int i, compile_time, link_time, total_compile_time, total_link_time;
2477ec3b29aSmrg
2487ec3b29aSmrg   if (!ShadersSupported())
2497ec3b29aSmrg      exit(1);
2507ec3b29aSmrg
2517ec3b29aSmrg   printf("Shader complexity: %d\n", complexity);
2527ec3b29aSmrg   printf("Num shaders: %d\n", num_shaders);
2537ec3b29aSmrg
2547ec3b29aSmrg   total_compile_time = total_link_time = 0;
2557ec3b29aSmrg
2567ec3b29aSmrg   /* create the shader programs */
2577ec3b29aSmrg   for (i = 0; i < num_shaders; i++) {
2587ec3b29aSmrg      char *fs_code = gen_large_shader(complexity, i);
2597ec3b29aSmrg
2607ec3b29aSmrg      if (verbose && i==0) {
2617ec3b29aSmrg         printf("Shader[0] code:\n%s\n", fs_code);
2627ec3b29aSmrg      }
2637ec3b29aSmrg
2647ec3b29aSmrg      programs[i] = create_shader_program(fs_code, &compile_time, &link_time);
2657ec3b29aSmrg      total_compile_time += compile_time;
2667ec3b29aSmrg      total_link_time += link_time;
2677ec3b29aSmrg
2687ec3b29aSmrg      free(fs_code);
2697ec3b29aSmrg   }
2707ec3b29aSmrg
2717ec3b29aSmrg   printf("Total glCompileShader() time: %d ms\n", total_compile_time);
2727ec3b29aSmrg   printf("Total glLinkProgram() time: %d ms\n", total_link_time);
2737ec3b29aSmrg
2747ec3b29aSmrg   glGenBuffers(1, &vbo);
2757ec3b29aSmrg   glBindBuffer(GL_ARRAY_BUFFER, vbo);
2767ec3b29aSmrg   glBufferData(GL_ARRAY_BUFFER, sizeof(coords), coords, GL_STATIC_DRAW);
2777ec3b29aSmrg   glVertexPointer(4, GL_FLOAT, 0, NULL);
2787ec3b29aSmrg   glEnable(GL_VERTEX_ARRAY);
2797ec3b29aSmrg}
2807ec3b29aSmrg
2817ec3b29aSmrg
2827ec3b29aSmrgint
2837ec3b29aSmrgmain(int argc, char *argv[])
2847ec3b29aSmrg{
2857ec3b29aSmrg   int i;
2867ec3b29aSmrg
2877ec3b29aSmrg   glutInit(&argc, argv);
2887ec3b29aSmrg   glutInitWindowSize(WinWidth, WinHeight);
2897ec3b29aSmrg   glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
2907ec3b29aSmrg   Win = glutCreateWindow(argv[0]);
2917ec3b29aSmrg   glewInit();
2927ec3b29aSmrg   glutReshapeFunc(Reshape);
2937ec3b29aSmrg   glutKeyboardFunc(Key);
2947ec3b29aSmrg   glutDisplayFunc(Draw);
2957ec3b29aSmrg
2967ec3b29aSmrg   for (i = 1; i < argc; i++) {
2977ec3b29aSmrg      if (strcmp(argv[i], "-v") == 0) {
2987ec3b29aSmrg         verbose = 1;
2997ec3b29aSmrg      }
3007ec3b29aSmrg      else if (strcmp(argv[i], "-c") == 0) {
3017ec3b29aSmrg         i++;
3027ec3b29aSmrg         complexity = atoi(argv[i]);
3037ec3b29aSmrg      }
3047ec3b29aSmrg      else if (strcmp(argv[i], "-n") == 0) {
3057ec3b29aSmrg         i++;
3067ec3b29aSmrg         num_shaders = atoi(argv[i]);
3077ec3b29aSmrg      }
3087ec3b29aSmrg      else {
3097ec3b29aSmrg         printf("unexpected option: %s\n", argv[i]);
3107ec3b29aSmrg         exit(1);
3117ec3b29aSmrg      }
3127ec3b29aSmrg   }
3137ec3b29aSmrg
3147ec3b29aSmrg   Init();
3157ec3b29aSmrg
3167ec3b29aSmrg   glutMainLoop();
3177ec3b29aSmrg   return 0;
3187ec3b29aSmrg}
319