1 /**
2  * Test compilation, link, draw time for very large shaders.
3  * Command line arguments:
4  *  -v    verbose output (print shader code)
5  *  -c N  generate shaders of complexity N
6  *  -n N  generate and draw with N shader programs
7  *
8  * Brian Paul
9  * 3 Dec 2015
10  */
11 
12 
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdbool.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19 #include <glad/glad.h>
20 #include "glut_wrap.h"
21 #include "shaderutil.h"
22 
23 #if defined(_MSC_VER)
24 #define snprintf _snprintf
25 #endif
26 
27 
28 #define MAX_PROGRAMS 10000
29 
30 static int Win;
31 static int WinWidth = 400, WinHeight = 400;
32 static int verbose = 0;
33 static int num_shaders = 1;
34 static int complexity = 5;
35 static int programs[MAX_PROGRAMS];
36 
37 static const float u1_val[4] = {.1, .2, .3, .4};
38 static const float u2_val[4] = {.6, .7, .8, .9};
39 
40 static const char *VS_code =
41    "void main()\n"
42    "{\n"
43    "  gl_Position = ftransform();\n"
44    "}\n";
45 
46 static const float coords[3][4] = {
47    {0, 0.1, 0, 1},
48    {0, 0, 0, 1},
49    {0, -0.1, 0, 1}
50 };
51 
52 #define NUM_POINTS (sizeof(coords) / sizeof(coords[0]))
53 
54 
55 
56 struct dynamic_string {
57    char *buffer;
58    unsigned len;
59    unsigned buffer_size;
60 };
61 
62 
63 static void
append_string(struct dynamic_string * ds,const char * s)64 append_string(struct dynamic_string *ds, const char *s)
65 {
66    int l = strlen(s);
67    if (ds->len + l >= ds->buffer_size) {
68       /* grow buffer */
69       int newsize = ds->buffer_size + l + 4096;
70       char *newbuf = malloc(newsize);
71       assert(newbuf);
72       if (ds->buffer)
73          strcpy(newbuf, ds->buffer);
74       free(ds->buffer);
75       ds->buffer = newbuf;
76       ds->buffer_size = newsize;
77    }
78    strcpy(ds->buffer + ds->len, s);
79    ds->len += l;
80    assert(strlen(ds->buffer) == ds->len);
81 }
82 
83 
84 /**
85  * Index is a term put into the shader code to make each shader a little
86  * different.
87  */
88 static char *
gen_large_shader(int num_functions,int index)89 gen_large_shader(int num_functions, int index)
90 {
91    int i;
92    struct dynamic_string ds = {0};
93    char s[100];
94 
95    append_string(&ds, "#version 120\n");
96    append_string(&ds, "\nuniform vec4 u1, u2;\n\n");
97 
98    for (i = 0; i < num_functions; i++) {
99       snprintf(s, sizeof(s), "vec4 func%d(vec4 a, float b)\n", i);
100       append_string(&ds, s);
101       append_string(&ds, "{\n");
102       if (i == 0) {
103          append_string(&ds, "   return a * b;\n");
104       }
105       else {
106          snprintf(s, sizeof(s),
107                   "   vec4 s = a * func%d(a, float(%d)) + vec4(b);\n",
108                   i-1, index);
109          append_string(&ds, s);
110 
111          snprintf(s, sizeof(s),
112                   "   vec4 t = a / func%d(a, 3.0) - vec4(b);\n", i-1);
113          append_string(&ds, s);
114 
115          if (i & 1) {
116             append_string(&ds, "   vec4 u = max(s, t);\n");
117          }
118          else {
119             /* use a conditional */
120             append_string(&ds, "   vec4 u = min(s, t);\n");
121             append_string(&ds, "   if (s.x > t.x) {\n");
122             snprintf(s, sizeof(s), "      u = vec4(%d);\n", i);
123             append_string(&ds, s);
124             append_string(&ds, "   }\n");
125          }
126 
127          append_string(&ds, "   return u;\n");
128       }
129 
130       append_string(&ds, "}\n\n");
131    }
132 
133    append_string(&ds, "void main()\n");
134    append_string(&ds, "{\n");
135    snprintf(s, sizeof(s), "   gl_FragColor = func%d(u1, u2.x);\n", i-1);
136    append_string(&ds, s);
137    append_string(&ds, "}\n");
138 
139    return ds.buffer;
140 }
141 
142 
143 static void
Draw(void)144 Draw(void)
145 {
146    int t0, t1;
147    int i;
148    int fixed_func_time = 0, glsl_time_1 = 0, glsl_time_2 = 0;
149 
150    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
151 
152    for (i = 0; i < num_shaders; i++) {
153       UseProgram(0);
154       t0 = glutGet(GLUT_ELAPSED_TIME);
155       glDrawArrays(GL_POINTS, 0, NUM_POINTS);
156       glFinish();
157       t1 = glutGet(GLUT_ELAPSED_TIME);
158       fixed_func_time += t1 - t0;
159 
160       UseProgram(programs[i]);
161       t0 = glutGet(GLUT_ELAPSED_TIME);
162       glDrawArrays(GL_POINTS, 0, NUM_POINTS);
163       glFinish();
164       t1 = glutGet(GLUT_ELAPSED_TIME);
165       glsl_time_1 += t1 - t0;
166 
167       t0 = glutGet(GLUT_ELAPSED_TIME);
168       glDrawArrays(GL_POINTS, 0, NUM_POINTS);
169       glFinish();
170       t1 = glutGet(GLUT_ELAPSED_TIME);
171       glsl_time_2 += t1 - t0;
172    }
173 
174    printf("Time to draw fixed-function points: %d ms\n", fixed_func_time);
175    printf("Time to draw 1st GLSL shader points: %d ms\n", glsl_time_1);
176    printf("Time to draw 2st GLSL shader points: %d ms\n", glsl_time_2);
177 
178    glutSwapBuffers();
179 }
180 
181 
182 static void
Reshape(int width,int height)183 Reshape(int width, int height)
184 {
185    WinWidth = width;
186    WinHeight = height;
187    glViewport(0, 0, width, height);
188    glMatrixMode(GL_PROJECTION);
189    glLoadIdentity();
190    glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
191    glMatrixMode(GL_MODELVIEW);
192    glLoadIdentity();
193    glTranslatef(0.0, 0.0, -15.0);
194 }
195 
196 
197 static void
Key(unsigned char key,int x,int y)198 Key(unsigned char key, int x, int y)
199 {
200    if (key == 27) {
201       glutDestroyWindow(Win);
202       exit(0);
203    }
204    glutPostRedisplay();
205 }
206 
207 
208 static GLuint
create_shader_program(const char * fs_code,int * compile_time,int * link_time)209 create_shader_program(const char *fs_code,
210                       int *compile_time, int *link_time)
211 {
212    GLuint fragShader;
213    GLuint vertShader;
214    GLuint program;
215    GLint u1_loc, u2_loc;
216    GLint t0, t1, t2;
217 
218    vertShader = CompileShaderText(GL_VERTEX_SHADER, VS_code);
219 
220    t0 = glutGet(GLUT_ELAPSED_TIME);
221    fragShader = CompileShaderText(GL_FRAGMENT_SHADER, fs_code);
222    t1 = glutGet(GLUT_ELAPSED_TIME);
223    program = LinkShaders(vertShader, fragShader);
224    t2 = glutGet(GLUT_ELAPSED_TIME);
225 
226    UseProgram(program);
227    u1_loc = glGetUniformLocation(program, "u1");
228    u2_loc = glGetUniformLocation(program, "u2");
229    glUniform4fv(u1_loc, 1, u1_val);
230    glUniform4fv(u2_loc, 1, u2_val);
231    UseProgram(0);
232 
233    *compile_time = t1 - t0;
234    *link_time = t2 - t1;
235 
236    assert(glGetError() == GL_NO_ERROR);
237 
238    return program;
239 }
240 
241 
242 static void
Init(void)243 Init(void)
244 {
245    GLuint vbo;
246    int i, compile_time, link_time, total_compile_time, total_link_time;
247 
248    if (!ShadersSupported())
249       exit(1);
250 
251    printf("Shader complexity: %d\n", complexity);
252    printf("Num shaders: %d\n", num_shaders);
253 
254    total_compile_time = total_link_time = 0;
255 
256    /* create the shader programs */
257    for (i = 0; i < num_shaders; i++) {
258       char *fs_code = gen_large_shader(complexity, i);
259 
260       if (verbose && i==0) {
261          printf("Shader[0] code:\n%s\n", fs_code);
262       }
263 
264       programs[i] = create_shader_program(fs_code, &compile_time, &link_time);
265       total_compile_time += compile_time;
266       total_link_time += link_time;
267 
268       free(fs_code);
269    }
270 
271    printf("Total glCompileShader() time: %d ms\n", total_compile_time);
272    printf("Total glLinkProgram() time: %d ms\n", total_link_time);
273 
274    glGenBuffers(1, &vbo);
275    glBindBuffer(GL_ARRAY_BUFFER, vbo);
276    glBufferData(GL_ARRAY_BUFFER, sizeof(coords), coords, GL_STATIC_DRAW);
277    glVertexPointer(4, GL_FLOAT, 0, NULL);
278    glEnable(GL_VERTEX_ARRAY);
279 }
280 
281 
282 int
main(int argc,char * argv[])283 main(int argc, char *argv[])
284 {
285    int i;
286 
287    glutInit(&argc, argv);
288    glutInitWindowSize(WinWidth, WinHeight);
289    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
290    Win = glutCreateWindow(argv[0]);
291    gladLoadGL();
292    glutReshapeFunc(Reshape);
293    glutKeyboardFunc(Key);
294    glutDisplayFunc(Draw);
295 
296    for (i = 1; i < argc; i++) {
297       if (strcmp(argv[i], "-v") == 0) {
298          verbose = 1;
299       }
300       else if (strcmp(argv[i], "-c") == 0) {
301          i++;
302          complexity = atoi(argv[i]);
303       }
304       else if (strcmp(argv[i], "-n") == 0) {
305          i++;
306          num_shaders = atoi(argv[i]);
307       }
308       else {
309          printf("unexpected option: %s\n", argv[i]);
310          exit(1);
311       }
312    }
313 
314    Init();
315 
316    glutMainLoop();
317    return 0;
318 }
319